diff --git a/cmd/resolutioncli/main.go b/cmd/resolutioncli/main.go index f368a720d..413c4a66f 100644 --- a/cmd/resolutioncli/main.go +++ b/cmd/resolutioncli/main.go @@ -151,24 +151,24 @@ func resolve(ctx context.Context, resolver *solver.DeppySolver, packageName stri return "", err } - bundleEntity, err := getBundleEntityFromSolution(solution, packageName) + bundle, err := bundleFromSolution(solution, packageName) if err != nil { return "", err } // Get the bundle image reference for the bundle - return bundleEntity.Image, nil + return bundle.Image, nil } -func getBundleEntityFromSolution(solution *solver.Solution, packageName string) (*catalogmetadata.Bundle, error) { +func bundleFromSolution(solution *solver.Solution, packageName string) (*catalogmetadata.Bundle, error) { for _, variable := range solution.SelectedVariables() { switch v := variable.(type) { case *olmvariables.BundleVariable: - entityPkgName := v.BundleEntity().Package - if packageName == entityPkgName { - return v.BundleEntity(), nil + bundlePkgName := v.Bundle().Package + if packageName == bundlePkgName { + return v.Bundle(), nil } } } - return nil, fmt.Errorf("entity for package %q not found in solution", packageName) + return nil, fmt.Errorf("bundle for package %q not found in solution", packageName) } diff --git a/go.mod b/go.mod index 05ac413e8..31aec6b42 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/onsi/ginkgo/v2 v2.12.0 github.com/onsi/gomega v1.27.10 github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42 - github.com/operator-framework/catalogd v0.5.0 + github.com/operator-framework/catalogd v0.6.0 github.com/operator-framework/deppy v0.0.1 github.com/operator-framework/operator-registry v1.28.0 github.com/operator-framework/rukpak v0.13.0 diff --git a/go.sum b/go.sum index 7c07ab4b7..4798ff1df 100644 --- a/go.sum +++ b/go.sum @@ -45,6 +45,7 @@ github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSY github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -73,6 +74,7 @@ github.com/Microsoft/hcsshim v0.9.6/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfy github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -265,6 +267,7 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= @@ -307,6 +310,7 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -456,6 +460,7 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M= github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= @@ -513,6 +518,7 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -523,6 +529,7 @@ github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4= github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c h1:fEE5/5VNnYUoBOj2I9TP8Jc+a7lge3QWn9DKE7NCwfc= github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c/go.mod h1:ObS/W+h8RYb1Y7fYivughjxojTmIu5iAIjSrSLCLeqE= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= @@ -555,6 +562,7 @@ github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52Cu github.com/joelanford/ignore v0.0.0-20210607151042-0d25dc18b62d h1:A2/B900ip/Z20TzkLeGRNy1s6J2HmH9AmGt+dHyqb4I= github.com/joelanford/ignore v0.0.0-20210607151042-0d25dc18b62d/go.mod h1:7HQupe4vyNxMKXmM5DFuwXHsqwMyglcYmZBtlDPIcZ8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -570,6 +578,7 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -625,6 +634,7 @@ github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= @@ -649,6 +659,7 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -663,6 +674,7 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= @@ -705,6 +717,8 @@ github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42 h1:d/Pnr github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42/go.mod h1:l/cuwtPxkVUY7fzYgdust2m9tlmb8I4pOvbsUufRb24= github.com/operator-framework/catalogd v0.5.0 h1:azU5KCofUXVoakXCPRGjAjYyDtZzNSSiSBWuZJLOn74= github.com/operator-framework/catalogd v0.5.0/go.mod h1:1xksKKGHglY0i92h3/KDZURMSEhf3xtKRj2vCfJ57fk= +github.com/operator-framework/catalogd v0.6.0 h1:dSZ54MVSHJ8hcoV7OCRxnk3x4O3ramlyPvvz0vsKYdk= +github.com/operator-framework/catalogd v0.6.0/go.mod h1:I0n086a4a+nP1YZy742IrPaWvOlWu0Mj6qA6j4K96Vg= github.com/operator-framework/deppy v0.0.1 h1:PLTtaFGwktPhKuKZkfUruTimrWpyaO3tghbsjs0uMjc= github.com/operator-framework/deppy v0.0.1/go.mod h1:EV6vnxRodSFRn2TFztfxFhMPGh5QufOhn3tpIP1Z8cc= github.com/operator-framework/operator-registry v1.28.0 h1:vtmd2WgJxkx7vuuOxW4k5Le/oo0SfonSeJVMU3rKIfk= @@ -795,6 +809,7 @@ github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -838,6 +853,7 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -851,6 +867,7 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= @@ -868,6 +885,7 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPS github.com/yvasiyarov/gorelic v0.0.7 h1:4DTF1WOM2ZZS/xMOkTFBOcb6XiHu/PKn3rVo6dbewQE= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9 h1:AsFN8kXcCVkUFHyuzp1FtYbzp1nCO/H6+1uPSGEyPzM= +gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -954,6 +972,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= diff --git a/internal/catalogmetadata/types_test.go b/internal/catalogmetadata/types_test.go index cc612d105..ec2dc3a96 100644 --- a/internal/catalogmetadata/types_test.go +++ b/internal/catalogmetadata/types_test.go @@ -123,7 +123,13 @@ func TestBundleRequiredPackages(t *testing.T) { } { t.Run(tt.name, func(t *testing.T) { packages, err := tt.bundle.RequiredPackages() - assert.Equal(t, tt.wantRequiredPackages, packages) + assert.Equal(t, len(tt.wantRequiredPackages), len(packages)) + for i, pkg := range packages { + // Must custom compare due to Semver function type + assert.Equal(t, tt.wantRequiredPackages[i].PackageRequired, pkg.PackageRequired) + assert.Equal(t, tt.wantRequiredPackages[i].PackageName, pkg.PackageName) + assert.Equal(t, tt.wantRequiredPackages[i].VersionRange, pkg.VersionRange) + } if tt.wantErr != "" { assert.EqualError(t, err, tt.wantErr) } else { diff --git a/internal/controllers/operator_controller.go b/internal/controllers/operator_controller.go index 1136e9467..3b0ef5067 100644 --- a/internal/controllers/operator_controller.go +++ b/internal/controllers/operator_controller.go @@ -154,9 +154,9 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha return ctrl.Result{}, unsat } - // lookup the bundle entity in the solution that corresponds to the + // lookup the bundle in the solution that corresponds to the // Operator's desired package name. - bundleEntity, err := r.getBundleEntityFromSolution(solution, op.Spec.PackageName) + bundle, err := r.bundleFromSolution(solution, op.Spec.PackageName) if err != nil { op.Status.InstalledBundleResource = "" setInstalledStatusConditionUnknown(&op.Status.Conditions, "installation has not been attempted as resolution failed", op.GetGeneration()) @@ -165,11 +165,11 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha return ctrl.Result{}, err } - // Now we can set the Resolved Condition, and the resolvedBundleSource field to the bundleEntity.Image value. - op.Status.ResolvedBundleResource = bundleEntity.Image - setResolvedStatusConditionSuccess(&op.Status.Conditions, fmt.Sprintf("resolved to %q", bundleEntity.Image), op.GetGeneration()) + // Now we can set the Resolved Condition, and the resolvedBundleSource field to the bundle.Image value. + op.Status.ResolvedBundleResource = bundle.Image + setResolvedStatusConditionSuccess(&op.Status.Conditions, fmt.Sprintf("resolved to %q", bundle.Image), op.GetGeneration()) - mediaType, err := bundleEntity.MediaType() + mediaType, err := bundle.MediaType() if err != nil { setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration()) return ctrl.Result{}, err @@ -181,7 +181,7 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha } // Ensure a BundleDeployment exists with its bundle source from the bundle // image we just looked up in the solution. - dep := r.generateExpectedBundleDeployment(*op, bundleEntity.Image, bundleProvisioner) + dep := r.generateExpectedBundleDeployment(*op, bundle.Image, bundleProvisioner) if err := r.ensureBundleDeployment(ctx, dep); err != nil { // originally Reason: operatorsv1alpha1.ReasonInstallationFailed op.Status.InstalledBundleResource = "" @@ -251,17 +251,17 @@ func mapBDStatusToInstalledCondition(existingTypedBundleDeployment *rukpakv1alph } } -func (r *OperatorReconciler) getBundleEntityFromSolution(solution *solver.Solution, packageName string) (*catalogmetadata.Bundle, error) { +func (r *OperatorReconciler) bundleFromSolution(solution *solver.Solution, packageName string) (*catalogmetadata.Bundle, error) { for _, variable := range solution.SelectedVariables() { switch v := variable.(type) { case *olmvariables.BundleVariable: - entityPkgName := v.BundleEntity().Package - if packageName == entityPkgName { - return v.BundleEntity(), nil + bundlePkgName := v.Bundle().Package + if packageName == bundlePkgName { + return v.Bundle(), nil } } } - return nil, fmt.Errorf("entity for package %q not found in solution", packageName) + return nil, fmt.Errorf("bundle for package %q not found in solution", packageName) } func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string, bundleProvisioner string) *unstructured.Unstructured { diff --git a/internal/controllers/operator_controller_test.go b/internal/controllers/operator_controller_test.go index bcc65caef..aba05399e 100644 --- a/internal/controllers/operator_controller_test.go +++ b/internal/controllers/operator_controller_test.go @@ -2,13 +2,14 @@ package controllers_test import ( "context" + "encoding/json" "fmt" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" "github.com/operator-framework/deppy/pkg/deppy/solver" + "github.com/operator-framework/operator-registry/alpha/declcfg" + "github.com/operator-framework/operator-registry/alpha/property" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -20,21 +21,25 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + "github.com/operator-framework/operator-controller/internal/catalogmetadata" "github.com/operator-framework/operator-controller/internal/conditionsets" "github.com/operator-framework/operator-controller/internal/controllers" + testutil "github.com/operator-framework/operator-controller/test/util" ) var _ = Describe("Operator Controller Test", func() { var ( - ctx context.Context - reconciler *controllers.OperatorReconciler + ctx context.Context + fakeCatalogClient testutil.FakeCatalogClient + reconciler *controllers.OperatorReconciler ) BeforeEach(func() { ctx = context.Background() + fakeCatalogClient = testutil.NewFakeCatalogClient(testBundleList) reconciler = &controllers.OperatorReconciler{ Client: cl, Scheme: sch, - Resolver: solver.NewDeppySolver(testEntitySource, controllers.NewVariableSource(cl)), + Resolver: solver.NewDeppySolver(controllers.NewVariableSource(cl, &fakeCatalogClient)), } }) When("the operator does not exist", func() { @@ -575,43 +580,6 @@ var _ = Describe("Operator Controller Test", func() { }) }) }) - When("the selected bundle's image ref cannot be parsed", func() { - const pkgName = "badimage" - BeforeEach(func() { - By("initializing cluster state") - operator = &operatorsv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{Name: opKey.Name}, - Spec: operatorsv1alpha1.OperatorSpec{PackageName: pkgName}, - } - err := cl.Create(ctx, operator) - Expect(err).NotTo(HaveOccurred()) - }) - It("sets resolution failure status and returns an error", func() { - By("running reconcile") - res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey}) - Expect(res).To(Equal(ctrl.Result{})) - Expect(err).To(MatchError(ContainSubstring(`error determining bundle path for entity`))) - - By("fetching updated operator after reconcile") - Expect(cl.Get(ctx, opKey, operator)).NotTo(HaveOccurred()) - - By("Checking the status fields") - Expect(operator.Status.ResolvedBundleResource).To(Equal("")) - Expect(operator.Status.InstalledBundleResource).To(Equal("")) - - By("checking the expected conditions") - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionFalse)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonResolutionFailed)) - Expect(cond.Message).To(ContainSubstring(`error determining bundle path for entity`)) - cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled) - Expect(cond).NotTo(BeNil()) - Expect(cond.Status).To(Equal(metav1.ConditionUnknown)) - Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown)) - Expect(cond.Message).To(Equal("installation has not been attempted as resolution failed")) - }) - }) When("the operator specifies a duplicate package", func() { const pkgName = "prometheus" var dupOperator *operatorsv1alpha1.Operator @@ -1080,6 +1048,71 @@ func verifyConditionsInvariants(op *operatorsv1alpha1.Operator) { } } +var betaChannel = catalogmetadata.Channel{Channel: declcfg.Channel{ + Name: "beta", + Entries: []declcfg.ChannelEntry{ + { + Name: "operatorhub/prometheus/0.37.0", + }, + { + Name: "operatorhub/prometheus/0.47.0", + Replaces: "operatorhub/prometheus/0.37.0", + }, + { + Name: "operatorhub/plain/0.1.0", + }, + { + Name: "operatorhub/badmedia/0.1.0", + }, + }, +}} + +var testBundleList = []*catalogmetadata.Bundle{ + {Bundle: declcfg.Bundle{ + Name: "operatorhub/prometheus/0.37.0", + Package: "prometheus", + Image: "quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35", + Properties: []property.Property{ + {Type: "olm.bundle.channelEntry", Value: json.RawMessage(`{"name":"prometheus.0.37.0"}`)}, + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"prometheus","version":"0.37.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[]`)}, + }, + }, InChannels: []*catalogmetadata.Channel{&betaChannel}}, + {Bundle: declcfg.Bundle{ + Name: "operatorhub/prometheus/0.47.0", + Package: "prometheus", + Image: "quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed", + Properties: []property.Property{ + {Type: "olm.bundle.channelEntry", Value: json.RawMessage(`{"name":"prometheus.0.47.0"}`)}, + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"prometheus","version":"0.47.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[]`)}, + }, + }, InChannels: []*catalogmetadata.Channel{&betaChannel}}, + {Bundle: declcfg.Bundle{ + Name: "operatorhub/plain/0.1.0", + Package: "plain", + Image: "quay.io/operatorhub/plain@sha256:plain", + Properties: []property.Property{ + {Type: "olm.bundle.channelEntry", Value: json.RawMessage(`{"name":"plain.0.1.0"}`)}, + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"plain","version":"0.1.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[]`)}, + {Type: "olm.bundle.mediatype", Value: json.RawMessage(`"plain+v0"`)}, + }, + }, InChannels: []*catalogmetadata.Channel{&betaChannel}}, + {Bundle: declcfg.Bundle{ + Name: "operatorhub/badmedia/0.1.0", + Package: "badmedia", + Image: "quay.io/operatorhub/badmedia@sha256:badmedia", + Properties: []property.Property{ + {Type: "olm.bundle.channelEntry", Value: json.RawMessage(`{"name":"badmedia.0.1.0"}`)}, + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"badmedia","version":"0.1.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[]`)}, + {Type: "olm.bundle.mediatype", Value: json.RawMessage(`"badmedia+v1"`)}, + }, + }, InChannels: []*catalogmetadata.Channel{&betaChannel}}, +} + +/* var testEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{ "operatorhub/prometheus/0.37.0": *input.NewEntity("operatorhub/prometheus/0.37.0", map[string]string{ "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35"`, @@ -1118,3 +1151,4 @@ var testEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{ "olm.bundle.mediatype": `"badmedia+v1"`, }), }) +*/ diff --git a/internal/resolution/variables/bundle.go b/internal/resolution/variables/bundle.go index 4be11a634..7b0568ffe 100644 --- a/internal/resolution/variables/bundle.go +++ b/internal/resolution/variables/bundle.go @@ -18,7 +18,7 @@ type BundleVariable struct { dependencies []*catalogmetadata.Bundle } -func (b *BundleVariable) BundleEntity() *catalogmetadata.Bundle { +func (b *BundleVariable) Bundle() *catalogmetadata.Bundle { return b.bundle } diff --git a/internal/resolution/variables/bundle_test.go b/internal/resolution/variables/bundle_test.go index 85d15b762..da29e3742 100644 --- a/internal/resolution/variables/bundle_test.go +++ b/internal/resolution/variables/bundle_test.go @@ -1,37 +1,55 @@ package variables_test import ( + "encoding/json" "testing" "github.com/operator-framework/deppy/pkg/deppy" "github.com/operator-framework/deppy/pkg/deppy/constraint" - "github.com/operator-framework/deppy/pkg/deppy/input" + "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" - olmentity "github.com/operator-framework/operator-controller/internal/resolution/entities" + "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" ) func TestBundleVariable(t *testing.T) { - bundleEntity := olmentity.NewBundleEntity(input.NewEntity("bundle-1", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })) - dependencies := []*olmentity.BundleEntity{ - olmentity.NewBundleEntity(input.NewEntity("bundle-2", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - olmentity.NewBundleEntity(input.NewEntity("bundle-3", map[string]string{ - property.TypePackage: `{"packageName": "test-package-3", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - } - bv := olmvariables.NewBundleVariable(bundleEntity, dependencies) - - if bv.BundleEntity() != bundleEntity { - t.Errorf("bundle entity '%v' does not match expected '%v'", bv.BundleEntity(), bundleEntity) + bundle := &catalogmetadata.Bundle{ + InChannels: []*catalogmetadata.Channel{ + {Channel: declcfg.Channel{Name: "stable"}}, + }, + Bundle: declcfg.Bundle{Name: "bundle-1", Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)}, + }}, } + dependencies := []*catalogmetadata.Bundle{ + { + InChannels: []*catalogmetadata.Channel{ + {Channel: declcfg.Channel{Name: "stable"}}, + }, + Bundle: declcfg.Bundle{Name: "bundle-2", Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "2.0.0"}`)}, + }}, + }, + { + InChannels: []*catalogmetadata.Channel{ + {Channel: declcfg.Channel{Name: "stable"}}, + }, + Bundle: declcfg.Bundle{Name: "bundle-3", Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "3.0.0"}`)}, + }}, + }, + } + ids := olmvariables.BundleToBundleVariableIDs(bundle) + if len(ids) != len(bundle.InChannels) { + t.Fatalf("bundle should produce one variable ID per channel; received: %d", len(bundle.InChannels)) + } + bv := olmvariables.NewBundleVariable(ids[0], bundle, dependencies) + + if bv.Bundle() != bundle { + t.Errorf("bundle '%v' does not match expected '%v'", bv.Bundle(), bundle) + } + for i, d := range bv.Dependencies() { if d != dependencies[i] { t.Errorf("dependency[%v] '%v' does not match expected '%v'", i, d, dependencies[i]) diff --git a/internal/resolution/variables/installed_package.go b/internal/resolution/variables/installed_package.go index 6ab86126c..35bd87a01 100644 --- a/internal/resolution/variables/installed_package.go +++ b/internal/resolution/variables/installed_package.go @@ -17,7 +17,7 @@ type InstalledPackageVariable struct { bundles []*catalogmetadata.Bundle } -func (r *InstalledPackageVariable) BundleEntities() []*catalogmetadata.Bundle { +func (r *InstalledPackageVariable) Bundles() []*catalogmetadata.Bundle { return r.bundles } diff --git a/internal/resolution/variables/installed_package_test.go b/internal/resolution/variables/installed_package_test.go index edcb9f5a8..9ac944f7b 100644 --- a/internal/resolution/variables/installed_package_test.go +++ b/internal/resolution/variables/installed_package_test.go @@ -1,43 +1,42 @@ package variables_test import ( + "encoding/json" "fmt" "testing" "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" + "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" - olmentity "github.com/operator-framework/operator-controller/internal/resolution/entities" + "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" ) func TestInstalledPackageVariable(t *testing.T) { packageName := "test-package" - bundleEntities := []*olmentity.BundleEntity{ - olmentity.NewBundleEntity(input.NewEntity("bundle-1", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - olmentity.NewBundleEntity(input.NewEntity("bundle-2", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - olmentity.NewBundleEntity(input.NewEntity("bundle-3", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "3.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), + + bundles := []*catalogmetadata.Bundle{ + {Bundle: declcfg.Bundle{Name: "bundle-1", Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)}, + }}}, + {Bundle: declcfg.Bundle{Name: "bundle-2", Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "2.0.0"}`)}, + }}}, + {Bundle: declcfg.Bundle{Name: "bundle-2", Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "3.0.0"}`)}, + }}}, } - ipv := olmvariables.NewInstalledPackageVariable(packageName, bundleEntities) + ipv := olmvariables.NewInstalledPackageVariable(packageName, bundles) id := deppy.IdentifierFromString(fmt.Sprintf("installed package %s", packageName)) if ipv.Identifier() != id { t.Errorf("package name '%v' does not match expected '%v'", ipv.Identifier(), id) } - for i, e := range ipv.BundleEntities() { - if e != bundleEntities[i] { - t.Errorf("bundle entity[%v] '%v' does not match expected '%v'", i, e, bundleEntities[i]) + for i, e := range ipv.Bundles() { + if e != bundles[i] { + t.Errorf("bundle[%v] '%v' does not match expected '%v'", i, e, bundles[i]) } } } diff --git a/internal/resolution/variables/required_package.go b/internal/resolution/variables/required_package.go index 3aff97743..368d09c6c 100644 --- a/internal/resolution/variables/required_package.go +++ b/internal/resolution/variables/required_package.go @@ -17,7 +17,7 @@ type RequiredPackageVariable struct { bundles []*catalogmetadata.Bundle } -func (r *RequiredPackageVariable) BundleEntities() []*catalogmetadata.Bundle { +func (r *RequiredPackageVariable) Bundles() []*catalogmetadata.Bundle { return r.bundles } diff --git a/internal/resolution/variables/required_package_test.go b/internal/resolution/variables/required_package_test.go index a0872fa6f..f0c09c2ed 100644 --- a/internal/resolution/variables/required_package_test.go +++ b/internal/resolution/variables/required_package_test.go @@ -1,43 +1,41 @@ package variables_test import ( + "encoding/json" "fmt" "testing" "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" + "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" - olmentity "github.com/operator-framework/operator-controller/internal/resolution/entities" + "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" ) func TestRequiredPackageVariable(t *testing.T) { packageName := "test-package" - bundleEntities := []*olmentity.BundleEntity{ - olmentity.NewBundleEntity(input.NewEntity("bundle-1", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - olmentity.NewBundleEntity(input.NewEntity("bundle-2", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - olmentity.NewBundleEntity(input.NewEntity("bundle-3", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "3.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), + bundles := []*catalogmetadata.Bundle{ + {Bundle: declcfg.Bundle{Name: "bundle-1", Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)}, + }}}, + {Bundle: declcfg.Bundle{Name: "bundle-2", Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "2.0.0"}`)}, + }}}, + {Bundle: declcfg.Bundle{Name: "bundle-2", Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "3.0.0"}`)}, + }}}, } - rpv := olmvariables.NewRequiredPackageVariable(packageName, bundleEntities) + rpv := olmvariables.NewRequiredPackageVariable(packageName, bundles) id := deppy.IdentifierFromString(fmt.Sprintf("required package %s", packageName)) if rpv.Identifier() != id { t.Errorf("package name '%v' does not match expected '%v'", rpv.Identifier(), id) } - for i, e := range rpv.BundleEntities() { - if e != bundleEntities[i] { - t.Errorf("bundle entity[%v] '%v' does not match expected '%v'", i, e, bundleEntities[i]) + for i, e := range rpv.Bundles() { + if e != bundles[i] { + t.Errorf("bundle entity[%v] '%v' does not match expected '%v'", i, e, bundles[i]) } } diff --git a/internal/resolution/variablesources/bundle_deployment_test.go b/internal/resolution/variablesources/bundle_deployment_test.go index 3eb82321a..c624be326 100644 --- a/internal/resolution/variablesources/bundle_deployment_test.go +++ b/internal/resolution/variablesources/bundle_deployment_test.go @@ -2,12 +2,17 @@ package variablesources_test import ( "context" + "encoding/json" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" "github.com/operator-framework/operator-controller/internal/resolution/variablesources" + testutil "github.com/operator-framework/operator-controller/test/util" + "github.com/operator-framework/operator-registry/alpha/declcfg" + "github.com/operator-framework/operator-registry/alpha/property" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -16,7 +21,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" ) @@ -47,42 +51,80 @@ func bundleDeployment(name, image string) *rukpakv1alpha1.BundleDeployment { } } -var BundleDeploymentTestEntityCache = map[deppy.Identifier]input.Entity{ - "operatorhub/prometheus/0.37.0": *input.NewEntity("operatorhub/prometheus/0.37.0", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35"`, - "olm.bundle.channelEntry": "{\"name\":\"prometheus.0.37.0\"}", - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.32.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"}, {\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.37.0\"}", - }), - "operatorhub/prometheus/0.47.0": *input.NewEntity("operatorhub/prometheus/0.47.0", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed"`, - "olm.bundle.channelEntry": "{\"replaces\":\"prometheus.0.37.0\", \"name\":\"prometheus.0.47.0\"}", - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.37.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"}, {\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1alpha1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.47.0\"}", - }), - "operatorhub/packageA/2.0.0": *input.NewEntity("operatorhub/packageA/2.0.0", map[string]string{ - "olm.bundle.path": `"foo.io/packageA/packageA:v2.0.0"`, - "olm.bundle.channelEntry": "{\"name\":\"packageA.2.0.0\"}", - "olm.channel": "{\"channelName\":\"stable\",\"priority\":0}", - "olm.gvk": "[{\"group\":\"foo.io\",\"kind\":\"Foo\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"packageA\",\"version\":\"2.0.0\"}", - }), -} - var _ = Describe("BundleDeploymentVariableSource", func() { - var bundleTestEntityCache input.EntitySource + var fakeCatalogClient testutil.FakeCatalogClient + var betaChannel catalogmetadata.Channel + var stableChannel catalogmetadata.Channel + var testBundleList []*catalogmetadata.Bundle BeforeEach(func() { - bundleTestEntityCache = input.NewCacheQuerier(BundleDeploymentTestEntityCache) + betaChannel = catalogmetadata.Channel{Channel: declcfg.Channel{ + Name: "beta", + Entries: []declcfg.ChannelEntry{ + { + Name: "operatorhub/prometheus/0.37.0", + Replaces: "operatorhub/prometheus/0.32.0", + }, + { + Name: "operatorhub/prometheus/0.47.0", + Replaces: "operatorhub/prometheus/0.37.0", + }, + }, + }} + + stableChannel = catalogmetadata.Channel{Channel: declcfg.Channel{ + Name: "beta", + Entries: []declcfg.ChannelEntry{ + { + Name: "operatorhub/packageA/2.0.0", + }, + }, + }} + + testBundleList = []*catalogmetadata.Bundle{ + {Bundle: declcfg.Bundle{ + Name: "operatorhub/prometheus/0.37.0", + Package: "prometheus", + Image: "quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35", + Properties: []property.Property{ + {Type: "olm.bundle.channelEntry", Value: json.RawMessage(`{"name":"prometheus.0.37.0"}`)}, + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"prometheus","version":"0.37.0"}`)}, + {Type: property.TypeChannel, Value: json.RawMessage(`{"channelName":"beta","priority":0,"replaces":"prometheusoperator.0.32.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"monitoring.coreos.com","kind":"Alertmanager","version":"v1"}, {"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1"}]`)}, + }, + }, InChannels: []*catalogmetadata.Channel{&betaChannel}}, + {Bundle: declcfg.Bundle{ + Name: "operatorhub/prometheus/0.47.0", + Package: "prometheus", + Image: "quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed", + Properties: []property.Property{ + {Type: "olm.bundle.channelEntry", Value: json.RawMessage(`{"replaces":"prometheus.0.37.0","name":"prometheus.0.47.0"}`)}, + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"prometheus","version":"0.47.0"}`)}, + {Type: property.TypeChannel, Value: json.RawMessage(`{"channelName":"beta","priority":0,"replaces":"prometheusoperator.0.37.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"monitoring.coreos.com","kind":"Alertmanager","version":"v1"}, {"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1alpha1"}]`)}, + }, + }, InChannels: []*catalogmetadata.Channel{&betaChannel}}, + {Bundle: declcfg.Bundle{ + Name: "operatorhub/packageA/2.0.0", + Package: "packageA", + Image: "foo.io/packageA/packageA:v2.0.0", + Properties: []property.Property{ + {Type: "olm.bundle.channelEntry", Value: json.RawMessage(`{"name":"packageA.2.0.0"}`)}, + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"packageA","version":"2.0.0"}`)}, + {Type: property.TypeChannel, Value: json.RawMessage(`{"channelName":"stable","priority":0}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + }, + }, InChannels: []*catalogmetadata.Channel{&stableChannel}}, + } + + fakeCatalogClient = testutil.NewFakeCatalogClient(testBundleList) }) It("should produce RequiredPackage variables", func() { cl := BundleDeploymentFakeClient(bundleDeployment("prometheus", "quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35")) - bdVariableSource := variablesources.NewBundleDeploymentVariableSource(cl, &MockRequiredPackageSource{}) - variables, err := bdVariableSource.GetVariables(context.Background(), bundleTestEntityCache) + bdVariableSource := variablesources.NewBundleDeploymentVariableSource(cl, &fakeCatalogClient, &MockRequiredPackageSource{}) + variables, err := bdVariableSource.GetVariables(context.Background()) Expect(err).ToNot(HaveOccurred()) installedPackageVariable := filterVariables[*olmvariables.InstalledPackageVariable](variables) @@ -90,7 +132,7 @@ var _ = Describe("BundleDeploymentVariableSource", func() { Expect(installedPackageVariable).To(WithTransform(func(bvars []*olmvariables.InstalledPackageVariable) map[deppy.Identifier]int { out := map[deppy.Identifier]int{} for _, variable := range bvars { - out[variable.Identifier()] = len(variable.BundleEntities()) + out[variable.Identifier()] = len(variable.Bundles()) } return out }, Equal(map[deppy.Identifier]int{ @@ -102,8 +144,8 @@ var _ = Describe("BundleDeploymentVariableSource", func() { It("should return an error if the bundleDeployment image doesn't match any operator resource", func() { cl := BundleDeploymentFakeClient(bundleDeployment("prometheus", "quay.io/operatorhubio/prometheus@sha256:nonexistent")) - bdVariableSource := variablesources.NewBundleDeploymentVariableSource(cl, &MockRequiredPackageSource{}) - _, err := bdVariableSource.GetVariables(context.Background(), bundleTestEntityCache) + bdVariableSource := variablesources.NewBundleDeploymentVariableSource(cl, &fakeCatalogClient, &MockRequiredPackageSource{}) + _, err := bdVariableSource.GetVariables(context.Background()) Expect(err.Error()).To(Equal("bundleImage \"quay.io/operatorhubio/prometheus@sha256:nonexistent\" not found")) }) }) diff --git a/internal/resolution/variablesources/bundles_and_dependencies.go b/internal/resolution/variablesources/bundles_and_dependencies.go index 5f3d6d136..5b15a4724 100644 --- a/internal/resolution/variablesources/bundles_and_dependencies.go +++ b/internal/resolution/variablesources/bundles_and_dependencies.go @@ -46,9 +46,9 @@ func (b *BundlesAndDepsVariableSource) GetVariables(ctx context.Context) ([]depp for _, variable := range variables { switch v := variable.(type) { case *olmvariables.RequiredPackageVariable: - bundleQueue = append(bundleQueue, v.BundleEntities()...) + bundleQueue = append(bundleQueue, v.Bundles()...) case *olmvariables.InstalledPackageVariable: - bundleQueue = append(bundleQueue, v.BundleEntities()...) + bundleQueue = append(bundleQueue, v.Bundles()...) } } @@ -74,7 +74,7 @@ func (b *BundlesAndDepsVariableSource) GetVariables(ctx context.Context) ([]depp // get bundle dependencies dependencies, err := b.filterBundleDependencies(allBundles, head) if err != nil { - return nil, fmt.Errorf("could not determine dependencies for entity with id '%s': %w", id, err) + return nil, fmt.Errorf("could not determine dependencies for bundle with id '%s': %w", id, err) } // add bundle dependencies to queue for processing diff --git a/internal/resolution/variablesources/bundles_and_dependencies_test.go b/internal/resolution/variablesources/bundles_and_dependencies_test.go index a7ed680d1..604ce5f95 100644 --- a/internal/resolution/variablesources/bundles_and_dependencies_test.go +++ b/internal/resolution/variablesources/bundles_and_dependencies_test.go @@ -2,155 +2,244 @@ package variablesources_test import ( "context" + "encoding/json" "errors" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" + "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" - olmentity "github.com/operator-framework/operator-controller/internal/resolution/entities" + "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" "github.com/operator-framework/operator-controller/internal/resolution/variablesources" + testutil "github.com/operator-framework/operator-controller/test/util" ) var _ = Describe("BundlesAndDepsVariableSource", func() { var ( - bdvs *variablesources.BundlesAndDepsVariableSource - mockEntitySource input.EntitySource + bdvs *variablesources.BundlesAndDepsVariableSource + testBundleList []*catalogmetadata.Bundle + fakeCatalogClient testutil.FakeCatalogClient ) BeforeEach(func() { + channel := catalogmetadata.Channel{Channel: declcfg.Channel{Name: "stable"}} + testBundleList = []*catalogmetadata.Bundle{ + // required package bundles + {Bundle: declcfg.Bundle{ + Name: "bundle-1", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + + {Bundle: declcfg.Bundle{ + Name: "bundle-2", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "2.0.0"}`)}, + {Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + {Type: property.TypePackageRequired, Value: json.RawMessage(`[{"packageName": "some-package", "versionRange": ">=1.0.0 <2.0.0"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + + // dependencies + {Bundle: declcfg.Bundle{ + Name: "bundle-4", + Package: "some-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "1.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-5", + Package: "some-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "1.5.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-6", + Package: "some-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "2.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-7", + Package: "some-other-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-other-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-8", + Package: "some-other-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-other-package", "version": "1.5.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + {Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"bar.io","kind":"Bar","version":"v1"}]`)}, + {Type: property.TypePackageRequired, Value: json.RawMessage(`[{"packageName": "another-package", "versionRange": "< 2.0.0"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + + // dependencies of dependencies + {Bundle: declcfg.Bundle{ + Name: "bundle-9", Package: "another-package", Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "another-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-10", + Package: "bar-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "bar-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"bar.io","kind":"Bar","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-11", + Package: "bar-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "bar-package", "version": "2.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"bar.io","kind":"Bar","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + + // test-package-2 required package - no dependencies + {Bundle: declcfg.Bundle{ + Name: "bundle-15", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "1.5.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-16", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "2.0.1"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-17", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "3.16.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + + // completely unrelated + {Bundle: declcfg.Bundle{ + Name: "bundle-12", + Package: "unrelated-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package", "version": "2.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-13", + Package: "unrelated-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package-2", "version": "2.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-14", + Package: "unrelated-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package-2", "version": "3.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + } + fakeCatalogClient = testutil.NewFakeCatalogClient(testBundleList) bdvs = variablesources.NewBundlesAndDepsVariableSource( + &fakeCatalogClient, &MockRequiredPackageSource{ ResultSet: []deppy.Variable{ - // must match data in mockEntitySource - olmvariables.NewRequiredPackageVariable("test-package", []*olmentity.BundleEntity{ - olmentity.NewBundleEntity(input.NewEntity("bundle-2", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVKRequired: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - property.TypePackageRequired: `[{"packageName": "some-package", "versionRange": ">=1.0.0 <2.0.0"}]`, - })), - olmentity.NewBundleEntity(input.NewEntity("bundle-1", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVKRequired: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - })), + // must match data in fakeCatalogClient + olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{ + {Bundle: declcfg.Bundle{ + Name: "bundle-2", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "2.0.0"}`)}, + {Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + {Type: property.TypePackageRequired, Value: json.RawMessage(`[{"packageName": "some-package", "versionRange": ">=1.0.0 <2.0.0"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-1", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, }), }, }, &MockRequiredPackageSource{ ResultSet: []deppy.Variable{ - // must match data in mockEntitySource - olmvariables.NewRequiredPackageVariable("test-package-2", []*olmentity.BundleEntity{ + // must match data in fakeCatalogClient + olmvariables.NewRequiredPackageVariable("test-package-2", []*catalogmetadata.Bundle{ // test-package-2 required package - no dependencies - olmentity.NewBundleEntity(input.NewEntity("bundle-15", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "1.5.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - olmentity.NewBundleEntity(input.NewEntity("bundle-16", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "2.0.1"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - olmentity.NewBundleEntity(input.NewEntity("bundle-17", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "3.16.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), + {Bundle: declcfg.Bundle{ + Name: "bundle-15", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "1.5.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-16", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "2.0.1"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-17", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "3.16.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, }), }, }, ) - mockEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{ - // required package bundles - "bundle-1": *input.NewEntity("bundle-1", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVKRequired: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - }), - "bundle-2": *input.NewEntity("bundle-2", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVKRequired: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - property.TypePackageRequired: `[{"packageName": "some-package", "versionRange": ">=1.0.0 <2.0.0"}]`, - }), - - // dependencies - "bundle-4": *input.NewEntity("bundle-4", map[string]string{ - property.TypePackage: `{"packageName": "some-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - "bundle-5": *input.NewEntity("bundle-5", map[string]string{ - property.TypePackage: `{"packageName": "some-package", "version": "1.5.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - "bundle-6": *input.NewEntity("bundle-6", map[string]string{ - property.TypePackage: `{"packageName": "some-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - "bundle-7": *input.NewEntity("bundle-7", map[string]string{ - property.TypePackage: `{"packageName": "some-other-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - }), - "bundle-8": *input.NewEntity("bundle-8", map[string]string{ - property.TypePackage: `{"packageName": "some-other-package", "version": "1.5.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - property.TypeGVKRequired: `[{"group":"bar.io","kind":"Bar","version":"v1"}]`, - property.TypePackageRequired: `[{"packageName": "another-package", "versionRange": "< 2.0.0"}]`, - }), - - // dependencies of dependencies - "bundle-9": *input.NewEntity("bundle-9", map[string]string{ - property.TypePackage: `{"packageName": "another-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - }), - "bundle-10": *input.NewEntity("bundle-10", map[string]string{ - property.TypePackage: `{"packageName": "bar-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"bar.io","kind":"Bar","version":"v1"}]`, - }), - "bundle-11": *input.NewEntity("bundle-11", map[string]string{ - property.TypePackage: `{"packageName": "bar-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"bar.io","kind":"Bar","version":"v1"}]`, - }), - - // test-package-2 required package - no dependencies - "bundle-15": *input.NewEntity("bundle-15", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "1.5.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - "bundle-16": *input.NewEntity("bundle-16", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "2.0.1"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - "bundle-17": *input.NewEntity("bundle-17", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "3.16.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - - // completely unrelated - "bundle-12": *input.NewEntity("bundle-12", map[string]string{ - property.TypePackage: `{"packageName": "unrelated-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - "bundle-13": *input.NewEntity("bundle-13", map[string]string{ - property.TypePackage: `{"packageName": "unrelated-package-2", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - "bundle-14": *input.NewEntity("bundle-14", map[string]string{ - property.TypePackage: `{"packageName": "unrelated-package-2", "version": "3.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - }) }) It("should return bundle variables with correct dependencies", func() { - variables, err := bdvs.GetVariables(context.TODO(), mockEntitySource) + variables, err := bdvs.GetVariables(context.TODO()) Expect(err).NotTo(HaveOccurred()) var bundleVariables []*olmvariables.BundleVariable @@ -166,34 +255,57 @@ var _ = Describe("BundlesAndDepsVariableSource", func() { Expect(bundleVariables).To(WithTransform(CollectBundleVariableIDs, Equal([]string{"bundle-2", "bundle-1", "bundle-15", "bundle-16", "bundle-17", "bundle-5", "bundle-4"}))) // check dependencies for one of the bundles - bundle2 := VariableWithID("bundle-2")(bundleVariables) + bundle2 := VariableWithName("bundle-2")(bundleVariables) // Note: As above, bundle-2 has GVK requirements satisfied by bundles 7, 8, and 9, but they // will not appear in this list as we are not currently taking Required GVKs into account - Expect(bundle2.Dependencies()).To(WithTransform(CollectDeppyEntities, Equal([]*input.Entity{ - input.NewEntity("bundle-5", map[string]string{ - property.TypePackage: `{"packageName": "some-package", "version": "1.5.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - input.NewEntity("bundle-4", map[string]string{ - property.TypePackage: `{"packageName": "some-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - }))) + Expect(bundle2.Dependencies()).To(HaveLen(2)) + Expect(bundle2.Dependencies()[0].Name).To(Equal("bundle-5")) + Expect(bundle2.Dependencies()[1].Name).To(Equal("bundle-4")) }) It("should return error if dependencies not found", func() { - mockEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{}) - _, err := bdvs.GetVariables(context.TODO(), mockEntitySource) + emptyCatalogClient := testutil.NewFakeCatalogClient(make([]*catalogmetadata.Bundle, 0)) + + bdvs = variablesources.NewBundlesAndDepsVariableSource( + &emptyCatalogClient, + &MockRequiredPackageSource{ + ResultSet: []deppy.Variable{ + // must match data in fakeCatalogClient + olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{ + {Bundle: declcfg.Bundle{ + Name: "bundle-2", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "2.0.0"}`)}, + {Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + {Type: property.TypePackageRequired, Value: json.RawMessage(`[{"packageName": "some-package", "versionRange": ">=1.0.0 <2.0.0"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{{Channel: declcfg.Channel{Name: "stable"}}}, + }, + {Bundle: declcfg.Bundle{ + Name: "bundle-1", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{{Channel: declcfg.Channel{Name: "stable"}}}, + }, + }), + }, + }, + ) + _, err := bdvs.GetVariables(context.TODO()) Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError("could not determine dependencies for entity with id 'bundle-2': could not find package dependencies for bundle 'bundle-2'")) + Expect(err.Error()).To(ContainSubstring("could not determine dependencies for bundle with id '-test-package-stable-bundle-2': could not find package dependencies for bundle 'bundle-2'")) }) It("should return error if an inner variable source returns an error", func() { bdvs = variablesources.NewBundlesAndDepsVariableSource( + &fakeCatalogClient, &MockRequiredPackageSource{Error: errors.New("fake error")}, ) - mockEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{}) - _, err := bdvs.GetVariables(context.TODO(), mockEntitySource) + _, err := bdvs.GetVariables(context.TODO()) Expect(err).To(HaveOccurred()) Expect(err).To(MatchError("fake error")) }) @@ -204,14 +316,14 @@ type MockRequiredPackageSource struct { Error error } -func (m *MockRequiredPackageSource) GetVariables(_ context.Context, _ input.EntitySource) ([]deppy.Variable, error) { +func (m *MockRequiredPackageSource) GetVariables(_ context.Context) ([]deppy.Variable, error) { return m.ResultSet, m.Error } -func VariableWithID(id deppy.Identifier) func(vars []*olmvariables.BundleVariable) *olmvariables.BundleVariable { +func VariableWithName(name string) func(vars []*olmvariables.BundleVariable) *olmvariables.BundleVariable { return func(vars []*olmvariables.BundleVariable) *olmvariables.BundleVariable { for i := 0; i < len(vars); i++ { - if vars[i].Identifier() == id { + if vars[i].Bundle().Name == name { return vars[i] } } @@ -222,15 +334,7 @@ func VariableWithID(id deppy.Identifier) func(vars []*olmvariables.BundleVariabl func CollectBundleVariableIDs(vars []*olmvariables.BundleVariable) []string { ids := make([]string, 0, len(vars)) for _, v := range vars { - ids = append(ids, v.Identifier().String()) + ids = append(ids, v.Bundle().Name) } return ids } - -func CollectDeppyEntities(vars []*olmentity.BundleEntity) []*input.Entity { - entities := make([]*input.Entity, 0, len(vars)) - for _, v := range vars { - entities = append(entities, v.Entity) - } - return entities -} diff --git a/internal/resolution/variablesources/crd_constraints.go b/internal/resolution/variablesources/crd_constraints.go index 2108a6317..746ed14fa 100644 --- a/internal/resolution/variablesources/crd_constraints.go +++ b/internal/resolution/variablesources/crd_constraints.go @@ -47,7 +47,7 @@ func (g *CRDUniquenessConstraintsVariableSource) GetVariables(ctx context.Contex for _, variable := range variables { switch v := variable.(type) { case *olmvariables.BundleVariable: - bundles := []*catalogmetadata.Bundle{v.BundleEntity()} + bundles := []*catalogmetadata.Bundle{v.Bundle()} bundles = append(bundles, v.Dependencies()...) for _, bundle := range bundles { for _, id := range olmvariables.BundleToBundleVariableIDs(bundle) { diff --git a/internal/resolution/variablesources/crd_constraints_test.go b/internal/resolution/variablesources/crd_constraints_test.go index d88489666..200d673f8 100644 --- a/internal/resolution/variablesources/crd_constraints_test.go +++ b/internal/resolution/variablesources/crd_constraints_test.go @@ -2,115 +2,181 @@ package variablesources_test import ( "context" + "encoding/json" "fmt" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" + "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" - olmentity "github.com/operator-framework/operator-controller/internal/resolution/entities" + "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" "github.com/operator-framework/operator-controller/internal/resolution/variablesources" ) -var bundleSet = map[deppy.Identifier]*input.Entity{ +var channel = catalogmetadata.Channel{Channel: declcfg.Channel{Name: "stable"}} +var bundleSet = map[string]*catalogmetadata.Bundle{ // required package bundles - "bundle-1": input.NewEntity("bundle-1", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVKRequired: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - property.TypeGVK: `[{"group":"bit.io","kind":"Bit","version":"v1"}]`, - }), - "bundle-2": input.NewEntity("bundle-2", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVKRequired: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - property.TypePackageRequired: `[{"packageName": "some-package", "versionRange": ">=1.0.0 <2.0.0"}]`, - property.TypeGVK: `[{"group":"bit.io","kind":"Bit","version":"v1"}]`, - }), + "bundle-1": {Bundle: declcfg.Bundle{ + Name: "bundle-1", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"bit.io","kind":"Bit","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-2": {Bundle: declcfg.Bundle{ + Name: "bundle-2", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "2.0.0"}`)}, + {Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + {Type: property.TypePackageRequired, Value: json.RawMessage(`[{"packageName": "some-package", "versionRange": ">=1.0.0 <2.0.0"}]`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"bit.io","kind":"Bit","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, // dependencies - "bundle-3": input.NewEntity("bundle-3", map[string]string{ - property.TypePackage: `{"packageName": "some-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"fiz.io","kind":"Fiz","version":"v1"}]`, - }), - "bundle-4": input.NewEntity("bundle-4", map[string]string{ - property.TypePackage: `{"packageName": "some-package", "version": "1.5.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"fiz.io","kind":"Fiz","version":"v1"}]`, - }), - "bundle-5": input.NewEntity("bundle-5", map[string]string{ - property.TypePackage: `{"packageName": "some-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"fiz.io","kind":"Fiz","version":"v1"}]`, - }), - "bundle-6": input.NewEntity("bundle-6", map[string]string{ - property.TypePackage: `{"packageName": "some-other-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - }), - "bundle-7": input.NewEntity("bundle-7", map[string]string{ - property.TypePackage: `{"packageName": "some-other-package", "version": "1.5.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - property.TypeGVKRequired: `[{"group":"bar.io","kind":"Bar","version":"v1"}]`, - property.TypePackageRequired: `[{"packageName": "another-package", "versionRange": "< 2.0.0"}]`, - }), + "bundle-3": {Bundle: declcfg.Bundle{ + Name: "bundle-3", + Package: "some-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"fiz.io","kind":"Fiz","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-4": {Bundle: declcfg.Bundle{ + Name: "bundle-4", + Package: "some-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "1.5.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"fiz.io","kind":"Fiz","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-5": {Bundle: declcfg.Bundle{ + Name: "bundle-5", + Package: "some-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-package", "version": "2.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"fiz.io","kind":"Fiz","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-6": {Bundle: declcfg.Bundle{ + Name: "bundle-6", + Package: "some-other-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-other-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-7": {Bundle: declcfg.Bundle{ + Name: "bundle-7", + Package: "some-other-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "some-other-package", "version": "1.5.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + {Type: property.TypeGVKRequired, Value: json.RawMessage(`[{"group":"bar.io","kind":"Bar","version":"v1"}]`)}, + {Type: property.TypePackageRequired, Value: json.RawMessage(`[{"packageName": "another-package", "versionRange": "< 2.0.0"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, // dependencies of dependencies - "bundle-8": input.NewEntity("bundle-8", map[string]string{ - property.TypePackage: `{"packageName": "another-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"foo.io","kind":"Foo","version":"v1"}]`, - }), - "bundle-9": input.NewEntity("bundle-9", map[string]string{ - property.TypePackage: `{"packageName": "bar-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"bar.io","kind":"Bar","version":"v1"}]`, - }), - "bundle-10": input.NewEntity("bundle-10", map[string]string{ - property.TypePackage: `{"packageName": "bar-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"bar.io","kind":"Bar","version":"v1"}]`, - }), + "bundle-8": {Bundle: declcfg.Bundle{ + Name: "bundle-8", + Package: "another-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "another-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-9": {Bundle: declcfg.Bundle{ + Name: "bundle-9", + Package: "bar-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "bar-package", "version": "1.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"bar.io","kind":"Bar","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-10": {Bundle: declcfg.Bundle{ + Name: "bundle-10", + Package: "bar-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "bar-package", "version": "2.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"bar.io","kind":"Bar","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, // test-package-2 required package - no dependencies - "bundle-14": input.NewEntity("bundle-14", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "1.5.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"buz.io","kind":"Buz","version":"v1"}]`, - }), - "bundle-15": input.NewEntity("bundle-15", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "2.0.1"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"buz.io","kind":"Buz","version":"v1"}]`, - }), - "bundle-16": input.NewEntity("bundle-16", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "3.16.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"buz.io","kind":"Buz","version":"v1"}]`, - }), + "bundle-14": {Bundle: declcfg.Bundle{ + Name: "bundle-14", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "1.5.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-15": {Bundle: declcfg.Bundle{ + Name: "bundle-15", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "2.0.1"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-16": {Bundle: declcfg.Bundle{ + Name: "bundle-16", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "3.16.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, // completely unrelated - "bundle-11": input.NewEntity("bundle-11", map[string]string{ - property.TypePackage: `{"packageName": "unrelated-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"buz.io","kind":"Buz","version":"v1alpha1"}]`, - }), - "bundle-12": input.NewEntity("bundle-12", map[string]string{ - property.TypePackage: `{"packageName": "unrelated-package-2", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"buz.io","kind":"Buz","version":"v1alpha1"}]`, - }), - "bundle-13": input.NewEntity("bundle-13", map[string]string{ - property.TypePackage: `{"packageName": "unrelated-package-2", "version": "3.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - property.TypeGVK: `[{"group":"buz.io","kind":"Buz","version":"v1alpha1"}]`, - }), + "bundle-11": {Bundle: declcfg.Bundle{ + Name: "bundle-11", + Package: "unrelated-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package", "version": "2.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1alpha1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-12": {Bundle: declcfg.Bundle{ + Name: "bundle-12", + Package: "unrelated-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package-2", "version": "2.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1alpha1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + "bundle-13": {Bundle: declcfg.Bundle{ + Name: "bundle-13", + Package: "unrelated-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "unrelated-package-2", "version": "3.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"buz.io","kind":"Buz","version":"v1alpha1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, } var _ = Describe("CRDUniquenessConstraintsVariableSource", func() { @@ -118,97 +184,93 @@ var _ = Describe("CRDUniquenessConstraintsVariableSource", func() { inputVariableSource *MockInputVariableSource crdConstraintVariableSource *variablesources.CRDUniquenessConstraintsVariableSource ctx context.Context - entitySource input.EntitySource ) BeforeEach(func() { inputVariableSource = &MockInputVariableSource{} crdConstraintVariableSource = variablesources.NewCRDUniquenessConstraintsVariableSource(inputVariableSource) ctx = context.Background() - - // the entity is not used in this variable source - entitySource = &PanicEntitySource{} }) It("should get variables from the input variable source and create global constraint variables", func() { inputVariableSource.ResultSet = []deppy.Variable{ - olmvariables.NewRequiredPackageVariable("test-package", []*olmentity.BundleEntity{ - olmentity.NewBundleEntity(bundleSet["bundle-2"]), - olmentity.NewBundleEntity(bundleSet["bundle-1"]), + olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{ + bundleSet["bundle-2"], + bundleSet["bundle-1"], }), - olmvariables.NewRequiredPackageVariable("test-package-2", []*olmentity.BundleEntity{ - olmentity.NewBundleEntity(bundleSet["bundle-14"]), - olmentity.NewBundleEntity(bundleSet["bundle-15"]), - olmentity.NewBundleEntity(bundleSet["bundle-16"]), + olmvariables.NewRequiredPackageVariable("test-package-2", []*catalogmetadata.Bundle{ + bundleSet["bundle-14"], + bundleSet["bundle-15"], + bundleSet["bundle-16"], }), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-2"]), - []*olmentity.BundleEntity{ - olmentity.NewBundleEntity(bundleSet["bundle-3"]), - olmentity.NewBundleEntity(bundleSet["bundle-4"]), - olmentity.NewBundleEntity(bundleSet["bundle-5"]), - olmentity.NewBundleEntity(bundleSet["bundle-6"]), - olmentity.NewBundleEntity(bundleSet["bundle-7"]), + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-2"])[0], + bundleSet["bundle-2"], + []*catalogmetadata.Bundle{ + bundleSet["bundle-3"], + bundleSet["bundle-4"], + bundleSet["bundle-5"], + bundleSet["bundle-6"], + bundleSet["bundle-7"], }, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-1"]), - []*olmentity.BundleEntity{ - olmentity.NewBundleEntity(bundleSet["bundle-6"]), - olmentity.NewBundleEntity(bundleSet["bundle-7"]), - olmentity.NewBundleEntity(bundleSet["bundle-8"]), + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-1"])[0], + bundleSet["bundle-1"], + []*catalogmetadata.Bundle{ + bundleSet["bundle-6"], + bundleSet["bundle-7"], + bundleSet["bundle-8"], }, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-3"]), - []*olmentity.BundleEntity{}, + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-3"])[0], + bundleSet["bundle-3"], + []*catalogmetadata.Bundle{}, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-4"]), - []*olmentity.BundleEntity{}, + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-4"])[0], + bundleSet["bundle-4"], + []*catalogmetadata.Bundle{}, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-5"]), - []*olmentity.BundleEntity{}, + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-5"])[0], + bundleSet["bundle-5"], + []*catalogmetadata.Bundle{}, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-6"]), - []*olmentity.BundleEntity{}, + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-6"])[0], + bundleSet["bundle-6"], + []*catalogmetadata.Bundle{}, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-7"]), - []*olmentity.BundleEntity{ - olmentity.NewBundleEntity(bundleSet["bundle-8"]), - olmentity.NewBundleEntity(bundleSet["bundle-9"]), - olmentity.NewBundleEntity(bundleSet["bundle-10"]), + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-7"])[0], + bundleSet["bundle-7"], + []*catalogmetadata.Bundle{ + bundleSet["bundle-8"], + bundleSet["bundle-9"], + bundleSet["bundle-10"], }, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-8"]), - []*olmentity.BundleEntity{}, + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-8"])[0], + bundleSet["bundle-8"], + []*catalogmetadata.Bundle{}, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-9"]), - []*olmentity.BundleEntity{}, + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-9"])[0], + bundleSet["bundle-9"], + []*catalogmetadata.Bundle{}, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-10"]), - []*olmentity.BundleEntity{}, + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-10"])[0], + bundleSet["bundle-10"], + []*catalogmetadata.Bundle{}, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-14"]), - []*olmentity.BundleEntity{}, + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-14"])[0], + bundleSet["bundle-14"], + []*catalogmetadata.Bundle{}, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-15"]), - []*olmentity.BundleEntity{}, + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-15"])[0], + bundleSet["bundle-15"], + []*catalogmetadata.Bundle{}, ), - olmvariables.NewBundleVariable( - olmentity.NewBundleEntity(bundleSet["bundle-16"]), - []*olmentity.BundleEntity{}, + olmvariables.NewBundleVariable(olmvariables.BundleToBundleVariableIDs(bundleSet["bundle-16"])[0], + bundleSet["bundle-16"], + []*catalogmetadata.Bundle{}, ), } - variables, err := crdConstraintVariableSource.GetVariables(ctx, entitySource) + variables, err := crdConstraintVariableSource.GetVariables(ctx) Expect(err).ToNot(HaveOccurred()) // Note: When accounting for GVK Uniqueness (which we are currently not doing), we // would expect to have 26 variables from the 5 unique GVKs (Bar, Bit, Buz, Fiz, Foo). @@ -235,38 +297,18 @@ var _ = Describe("CRDUniquenessConstraintsVariableSource", func() { It("should return an error if input variable source returns an error", func() { inputVariableSource = &MockInputVariableSource{Err: fmt.Errorf("error getting variables")} crdConstraintVariableSource = variablesources.NewCRDUniquenessConstraintsVariableSource(inputVariableSource) - _, err := crdConstraintVariableSource.GetVariables(ctx, entitySource) + _, err := crdConstraintVariableSource.GetVariables(ctx) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("error getting variables")) }) }) -var _ input.EntitySource = &PanicEntitySource{} - -type PanicEntitySource struct{} - -func (p PanicEntitySource) Get(_ context.Context, _ deppy.Identifier) (*input.Entity, error) { - return nil, fmt.Errorf("if you are seeing this it is because the global variable source is calling the entity source - this shouldn't happen") -} - -func (p PanicEntitySource) Filter(_ context.Context, _ input.Predicate) (input.EntityList, error) { - return nil, fmt.Errorf("if you are seeing this it is because the global variable source is calling the entity source - this shouldn't happen") -} - -func (p PanicEntitySource) GroupBy(_ context.Context, _ input.GroupByFunction) (input.EntityListMap, error) { - return nil, fmt.Errorf("if you are seeing this it is because the global variable source is calling the entity source - this shouldn't happen") -} - -func (p PanicEntitySource) Iterate(_ context.Context, _ input.IteratorFunction) error { - return fmt.Errorf("if you are seeing this it is because the global variable source is calling the entity source - this shouldn't happen") -} - type MockInputVariableSource struct { ResultSet []deppy.Variable Err error } -func (m *MockInputVariableSource) GetVariables(_ context.Context, _ input.EntitySource) ([]deppy.Variable, error) { +func (m *MockInputVariableSource) GetVariables(_ context.Context) ([]deppy.Variable, error) { if m.Err != nil { return nil, m.Err } diff --git a/internal/resolution/variablesources/installed_package.go b/internal/resolution/variablesources/installed_package.go index 0dbee55ad..76445cb13 100644 --- a/internal/resolution/variablesources/installed_package.go +++ b/internal/resolution/variablesources/installed_package.go @@ -27,7 +27,7 @@ func (r *InstalledPackageVariableSource) GetVariables(ctx context.Context) ([]de return nil, err } - // find corresponding bundle entity for the installed content + // find corresponding bundle for the installed content resultSet := catalogfilter.Filter(allBundles, catalogfilter.WithBundleImage(r.bundleImage)) if len(resultSet) == 0 { return nil, r.notFoundError() diff --git a/internal/resolution/variablesources/installed_package_test.go b/internal/resolution/variablesources/installed_package_test.go index 5581039a4..b224f3714 100644 --- a/internal/resolution/variablesources/installed_package_test.go +++ b/internal/resolution/variablesources/installed_package_test.go @@ -2,75 +2,116 @@ package variablesources_test import ( "context" + "encoding/json" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" + "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" - olmentity "github.com/operator-framework/operator-controller/internal/resolution/entities" + "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" "github.com/operator-framework/operator-controller/internal/resolution/variablesources" + testutil "github.com/operator-framework/operator-controller/test/util" ) var _ = Describe("InstalledPackageVariableSource", func() { var ( - ipvs *variablesources.InstalledPackageVariableSource - bundleImage string - mockEntitySource input.EntitySource + ipvs *variablesources.InstalledPackageVariableSource + fakeCatalogClient testutil.FakeCatalogClient + bundleImage string ) BeforeEach(func() { + channel := catalogmetadata.Channel{Channel: declcfg.Channel{ + Name: "stable", + Entries: []declcfg.ChannelEntry{ + { + Name: "test-package.v1.0.0", + }, + { + Name: "test-package.v2.0.0", + Replaces: "test-package.v1.0.0", + }, + { + Name: "test-package.v3.0.0", + Replaces: "test-package.v2.0.0", + }, + { + Name: "test-package.v4.0.0", + Replaces: "test-package.v3.0.0", + }, + { + Name: "test-package.v5.0.0", + Replaces: "test-package.v4.0.0", + }, + }, + }} + bundleList := []*catalogmetadata.Bundle{ + {Bundle: declcfg.Bundle{ + Name: "test-package.v1.0.0", + Package: "test-package", + Image: "registry.io/repo/test-package@v1.0.0", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "test-package.v3.0.0", + Package: "test-package", + Image: "registry.io/repo/test-package@v3.0.0", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "3.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "test-package.v2.0.0", + Package: "test-package", + Image: "registry.io/repo/test-package@v2.0.0", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "2.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "test-package.v4.0.0", + Package: "test-package", + Image: "registry.io/repo/test-package@v4.0.0", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "4.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "test-package.v5.0.0", + Package: "test-package", + Image: "registry.io/repo/test-package@v5.0.0", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "5-0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + } var err error bundleImage = "registry.io/repo/test-package@v2.0.0" - ipvs, err = variablesources.NewInstalledPackageVariableSource(bundleImage) + fakeCatalogClient = testutil.NewFakeCatalogClient(bundleList) + ipvs, err = variablesources.NewInstalledPackageVariableSource(&fakeCatalogClient, bundleImage) Expect(err).NotTo(HaveOccurred()) - - mockEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{ - "test-package.v1.0.0": *input.NewEntity("test-package.v1.0.0test-packagestable", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - olmentity.PropertyBundlePath: `"registry.io/repo/test-package@v1.0.0"`, - olmentity.PropertyBundleChannelEntry: `{"name": "test-package.v1.0.0"}`, - }), - "test-package.v3.0.0": *input.NewEntity("test-package.v3.0.0test-packagestable", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "3.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - olmentity.PropertyBundlePath: `"registry.io/repo/test-package@v3.0.0"`, - olmentity.PropertyBundleChannelEntry: `{"name": "test-package.v3.0.0","replaces": "test-package.v2.0.0"}`, - }), - "test-package.v2.0.0": *input.NewEntity("test-package.v2.0.0test-packagestable", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - olmentity.PropertyBundlePath: `"registry.io/repo/test-package@v2.0.0"`, - olmentity.PropertyBundleChannelEntry: `{"name": "test-package.v2.0.0","replaces": "test-package.v1.0.0"}`, - }), - "test-package.4.0.0": *input.NewEntity("test-package.v4.0.0test-packagestable", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "4.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - olmentity.PropertyBundlePath: `"registry.io/repo/test-package@v4.0.0"`, - olmentity.PropertyBundleChannelEntry: `{"name": "test-package.v4.0.0","replaces": "test-package.v3.0.0"}`, - }), - "test-package.5.0.0": *input.NewEntity("test-package.v5.0.0test-packagestable", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "5-00"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - olmentity.PropertyBundlePath: `"registry.io/repo/test-package@v5.0.0"`, - olmentity.PropertyBundleChannelEntry: `{"name": "test-package.v5.0.0","replaces": "test-package.v4.0.0"}`, - }), - }) }) It("should return the correct package variable", func() { - variables, err := ipvs.GetVariables(context.TODO(), mockEntitySource) + variables, err := ipvs.GetVariables(context.TODO()) Expect(err).NotTo(HaveOccurred()) Expect(variables).To(HaveLen(1)) reqPackageVar, ok := variables[0].(*olmvariables.InstalledPackageVariable) Expect(ok).To(BeTrue()) Expect(reqPackageVar.Identifier()).To(Equal(deppy.IdentifierFromString("installed package test-package"))) - // ensure bundle entities are in version order (high to low) - Expect(reqPackageVar.BundleEntities()[0].ID).To(Equal(deppy.IdentifierFromString("test-package.v3.0.0test-packagestable"))) - Expect(reqPackageVar.BundleEntities()[1].ID).To(Equal(deppy.IdentifierFromString("test-package.v2.0.0test-packagestable"))) + // ensure bundles are in version order (high to low) + Expect(reqPackageVar.Bundles()[0].Name).To(Equal("test-package.v3.0.0")) + Expect(reqPackageVar.Bundles()[1].Name).To(Equal("test-package.v2.0.0")) }) }) diff --git a/internal/resolution/variablesources/operator_test.go b/internal/resolution/variablesources/operator_test.go index 0920b4961..e7d712449 100644 --- a/internal/resolution/variablesources/operator_test.go +++ b/internal/resolution/variablesources/operator_test.go @@ -2,21 +2,28 @@ package variablesources_test import ( "context" - "fmt" + "encoding/json" + "errors" + + "github.com/operator-framework/deppy/pkg/deppy" + "github.com/operator-framework/operator-registry/alpha/declcfg" + "github.com/operator-framework/operator-registry/alpha/property" . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" - operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" + testutil "github.com/operator-framework/operator-controller/test/util" + + "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" "github.com/operator-framework/operator-controller/internal/resolution/variablesources" ) @@ -27,27 +34,6 @@ func FakeClient(objects ...client.Object) client.Client { return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build() } -var testEntityCache = map[deppy.Identifier]input.Entity{ - "operatorhub/prometheus/0.37.0": *input.NewEntity("operatorhub/prometheus/0.37.0", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35"`, - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.32.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"}, {\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.37.0\"}", - }), - "operatorhub/prometheus/0.47.0": *input.NewEntity("operatorhub/prometheus/0.47.0", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed"`, - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.37.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"}, {\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1alpha1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.47.0\"}", - }), - "operatorhub/packageA/2.0.0": *input.NewEntity("operatorhub/packageA/2.0.0", map[string]string{ - "olm.bundle.path": `"foo.io/packageA/packageA:v2.0.0"`, - "olm.channel": "{\"channelName\":\"stable\",\"priority\":0}", - "olm.gvk": "[{\"group\":\"foo.io\",\"kind\":\"Foo\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"packageA\",\"version\":\"2.0.0\"}", - }), -} - func operator(name string) *operatorsv1alpha1.Operator { return &operatorsv1alpha1.Operator{ ObjectMeta: metav1.ObjectMeta{ @@ -60,17 +46,78 @@ func operator(name string) *operatorsv1alpha1.Operator { } var _ = Describe("OperatorVariableSource", func() { - var testEntitySource input.EntitySource + var betaChannel catalogmetadata.Channel + var stableChannel catalogmetadata.Channel + var testBundleList []*catalogmetadata.Bundle BeforeEach(func() { - testEntitySource = input.NewCacheQuerier(testEntityCache) + betaChannel = catalogmetadata.Channel{ + Channel: declcfg.Channel{ + Name: "beta", + Entries: []declcfg.ChannelEntry{ + { + Name: "operatorhub/prometheus/0.37.0", + Replaces: "operatorhub/prometheus/0.32.0", + }, + { + Name: "operatorhub/prometheus/0.47.0", + Replaces: "operatorhub/prometheus/0.37.0", + }, + }, + }, + } + + stableChannel = catalogmetadata.Channel{ + Channel: declcfg.Channel{ + Name: "stable", + Entries: []declcfg.ChannelEntry{ + { + Name: "operatorhub/packageA/2.0.0", + }, + }, + }, + } + + testBundleList = []*catalogmetadata.Bundle{ + {Bundle: declcfg.Bundle{ + Name: "operatorhub/prometheus/0.37.0", + Package: "prometheus", + Image: "quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"prometheus","version":"0.37.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"monitoring.coreos.com","kind":"Alertmanager","version":"v1"}, {"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&betaChannel}, + }, + {Bundle: declcfg.Bundle{ + Name: "operatorhub/prometheus/0.47.0", + Package: "prometheus", + Image: "quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"prometheus","version":"0.47.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"monitoring.coreos.com","kind":"Alertmanager","version":"v1"}, {"group":"monitoring.coreos.com","kind":"Prometheus","version":"v1alpha1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&betaChannel}, + }, + {Bundle: declcfg.Bundle{ + Name: "operatorhub/packageA/2.0.0", + Package: "packageA", + Image: "foo.io/packageA/packageA:v2.0.0", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName":"packageA","version":"2.0.0"}`)}, + {Type: property.TypeGVK, Value: json.RawMessage(`[{"group":"foo.io","kind":"Foo","version":"v1"}]`)}, + }}, + InChannels: []*catalogmetadata.Channel{&stableChannel}, + }, + } + }) It("should produce RequiredPackage variables", func() { cl := FakeClient(operator("prometheus"), operator("packageA")) - - opVariableSource := variablesources.NewOperatorVariableSource(cl, &MockRequiredPackageSource{}) - variables, err := opVariableSource.GetVariables(context.Background(), testEntitySource) + fakeCatalogClient := testutil.NewFakeCatalogClient(testBundleList) + opVariableSource := variablesources.NewOperatorVariableSource(cl, &fakeCatalogClient, &MockRequiredPackageSource{}) + variables, err := opVariableSource.GetVariables(context.Background()) Expect(err).ToNot(HaveOccurred()) packageRequiredVariables := filterVariables[*olmvariables.RequiredPackageVariable](variables) @@ -78,7 +125,7 @@ var _ = Describe("OperatorVariableSource", func() { Expect(packageRequiredVariables).To(WithTransform(func(bvars []*olmvariables.RequiredPackageVariable) map[deppy.Identifier]int { out := map[deppy.Identifier]int{} for _, variable := range bvars { - out[variable.Identifier()] = len(variable.BundleEntities()) + out[variable.Identifier()] = len(variable.Bundles()) } return out }, Equal(map[deppy.Identifier]int{ @@ -89,34 +136,14 @@ var _ = Describe("OperatorVariableSource", func() { It("should return an errors when they occur", func() { cl := FakeClient(operator("prometheus"), operator("packageA")) + fakeCatalogClient := testutil.NewFakeCatalogClientWithError(errors.New("something bad happened!")) - opVariableSource := variablesources.NewOperatorVariableSource(cl, nil) - _, err := opVariableSource.GetVariables(context.Background(), FailEntitySource{}) + opVariableSource := variablesources.NewOperatorVariableSource(cl, &fakeCatalogClient, nil) + _, err := opVariableSource.GetVariables(context.Background()) Expect(err).To(HaveOccurred()) }) }) -var _ input.EntitySource = &FailEntitySource{} - -type FailEntitySource struct { -} - -func (f FailEntitySource) Get(_ context.Context, _ deppy.Identifier) (*input.Entity, error) { - return nil, fmt.Errorf("error executing get") -} - -func (f FailEntitySource) Filter(_ context.Context, _ input.Predicate) (input.EntityList, error) { - return nil, fmt.Errorf("error executing filter") -} - -func (f FailEntitySource) GroupBy(_ context.Context, _ input.GroupByFunction) (input.EntityListMap, error) { - return nil, fmt.Errorf("error executing group by") -} - -func (f FailEntitySource) Iterate(_ context.Context, _ input.IteratorFunction) error { - return fmt.Errorf("error executing iterate") -} - func filterVariables[D deppy.Variable](variables []deppy.Variable) []D { var out []D for _, variable := range variables { diff --git a/internal/resolution/variablesources/required_package_test.go b/internal/resolution/variablesources/required_package_test.go index 834054c70..f43ba99f1 100644 --- a/internal/resolution/variablesources/required_package_test.go +++ b/internal/resolution/variablesources/required_package_test.go @@ -2,118 +2,136 @@ package variablesources_test import ( "context" + "encoding/json" + "errors" "fmt" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/operator-framework/deppy/pkg/deppy" - "github.com/operator-framework/deppy/pkg/deppy/input" + "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" - olmentity "github.com/operator-framework/operator-controller/internal/resolution/entities" + "github.com/operator-framework/operator-controller/internal/catalogmetadata" olmvariables "github.com/operator-framework/operator-controller/internal/resolution/variables" "github.com/operator-framework/operator-controller/internal/resolution/variablesources" + testutil "github.com/operator-framework/operator-controller/test/util" ) var _ = Describe("RequiredPackageVariableSource", func() { var ( - rpvs *variablesources.RequiredPackageVariableSource - packageName string - mockEntitySource input.EntitySource + rpvs *variablesources.RequiredPackageVariableSource + fakeCatalogClient testutil.FakeCatalogClient + packageName string ) BeforeEach(func() { var err error packageName = "test-package" - rpvs, err = variablesources.NewRequiredPackageVariableSource(packageName) - Expect(err).NotTo(HaveOccurred()) - mockEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{ - "bundle-1": *input.NewEntity("bundle-1", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - "bundle-2": *input.NewEntity("bundle-2", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "3.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - "bundle-3": *input.NewEntity("bundle-3", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - + channel := catalogmetadata.Channel{Channel: declcfg.Channel{ + Name: "stable", + }} + bundleList := []*catalogmetadata.Bundle{ + {Bundle: declcfg.Bundle{ + Name: "test-package.v1.0.0", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "1.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "test-package.v3.0.0", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "3.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "test-package.v2.0.0", + Package: "test-package", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package", "version": "2.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, // add some bundles from a different package - "bundle-4": *input.NewEntity("bundle-4", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - "bundle-5": *input.NewEntity("bundle-5", map[string]string{ - property.TypePackage: `{"packageName": "test-package-2", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - }), - }) + {Bundle: declcfg.Bundle{ + Name: "test-package-2.v1.0.0", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "1.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + {Bundle: declcfg.Bundle{ + Name: "test-package-2.v2.0.0", + Package: "test-package-2", + Properties: []property.Property{ + {Type: property.TypePackage, Value: json.RawMessage(`{"packageName": "test-package-2", "version": "2.0.0"}`)}, + }}, + InChannels: []*catalogmetadata.Channel{&channel}, + }, + } + fakeCatalogClient = testutil.NewFakeCatalogClient(bundleList) + rpvs, err = variablesources.NewRequiredPackageVariableSource(&fakeCatalogClient, packageName) + Expect(err).NotTo(HaveOccurred()) }) It("should return the correct package variable", func() { - variables, err := rpvs.GetVariables(context.TODO(), mockEntitySource) + variables, err := rpvs.GetVariables(context.TODO()) Expect(err).NotTo(HaveOccurred()) Expect(variables).To(HaveLen(1)) reqPackageVar, ok := variables[0].(*olmvariables.RequiredPackageVariable) Expect(ok).To(BeTrue()) Expect(reqPackageVar.Identifier()).To(Equal(deppy.IdentifierFromString(fmt.Sprintf("required package %s", packageName)))) - - // ensure bundle entities are in version order (high to low) - Expect(reqPackageVar.BundleEntities()).To(Equal([]*olmentity.BundleEntity{ - olmentity.NewBundleEntity(input.NewEntity("bundle-2", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "3.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - olmentity.NewBundleEntity(input.NewEntity("bundle-3", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - olmentity.NewBundleEntity(input.NewEntity("bundle-1", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`})), - })) + Expect(reqPackageVar.Bundles()).To(HaveLen(3)) + // ensure bundles are in version order (high to low) + Expect(reqPackageVar.Bundles()[0].Name).To(Equal("test-package.v3.0.0")) + Expect(reqPackageVar.Bundles()[1].Name).To(Equal("test-package.v2.0.0")) + Expect(reqPackageVar.Bundles()[2].Name).To(Equal("test-package.v1.0.0")) }) It("should filter by version range", func() { // recreate source with version range option var err error - rpvs, err = variablesources.NewRequiredPackageVariableSource(packageName, variablesources.InVersionRange(">=1.0.0 !=2.0.0 <3.0.0")) + rpvs, err = variablesources.NewRequiredPackageVariableSource(&fakeCatalogClient, packageName, variablesources.InVersionRange(">=1.0.0 !=2.0.0 <3.0.0")) Expect(err).NotTo(HaveOccurred()) - variables, err := rpvs.GetVariables(context.TODO(), mockEntitySource) + variables, err := rpvs.GetVariables(context.TODO()) Expect(err).NotTo(HaveOccurred()) Expect(variables).To(HaveLen(1)) reqPackageVar, ok := variables[0].(*olmvariables.RequiredPackageVariable) Expect(ok).To(BeTrue()) Expect(reqPackageVar.Identifier()).To(Equal(deppy.IdentifierFromString(fmt.Sprintf("required package %s", packageName)))) - // ensure bundle entities are in version order (high to low) - Expect(reqPackageVar.BundleEntities()).To(Equal([]*olmentity.BundleEntity{ - olmentity.NewBundleEntity(input.NewEntity("bundle-1", map[string]string{ - property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, - property.TypeChannel: `{"channelName":"stable","priority":0}`, - })), - })) + Expect(reqPackageVar.Bundles()).To(HaveLen(1)) + // test-package.v1.0.0 is the only package that matches the provided filter + Expect(reqPackageVar.Bundles()[0].Name).To(Equal("test-package.v1.0.0")) }) It("should fail with bad semver range", func() { - _, err := variablesources.NewRequiredPackageVariableSource(packageName, variablesources.InVersionRange("not a valid semver")) + _, err := variablesources.NewRequiredPackageVariableSource(&fakeCatalogClient, packageName, variablesources.InVersionRange("not a valid semver")) Expect(err).To(HaveOccurred()) }) It("should return an error if package not found", func() { - mockEntitySource := input.NewCacheQuerier(map[deppy.Identifier]input.Entity{}) - _, err := rpvs.GetVariables(context.TODO(), mockEntitySource) + emptyCatalogClient := testutil.NewFakeCatalogClient([]*catalogmetadata.Bundle{}) + rpvs, err := variablesources.NewRequiredPackageVariableSource(&emptyCatalogClient, packageName) + Expect(err).NotTo(HaveOccurred()) + _, err = rpvs.GetVariables(context.TODO()) Expect(err).To(HaveOccurred()) Expect(err).To(MatchError("package 'test-package' not found")) }) - It("should return an error if entity source errors", func() { - _, err := rpvs.GetVariables(context.TODO(), FailEntitySource{}) + It("should return an error if catalog client errors", func() { + testError := errors.New("something bad happened!") + emptyCatalogClient := testutil.NewFakeCatalogClientWithError(testError) + rpvs, err := variablesources.NewRequiredPackageVariableSource(&emptyCatalogClient, packageName, variablesources.InVersionRange(">=1.0.0 !=2.0.0 <3.0.0")) + Expect(err).NotTo(HaveOccurred()) + _, err = rpvs.GetVariables(context.TODO()) Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError("error executing filter")) + Expect(err).To(MatchError(testError)) }) }) diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 3d970321d..eed98f3bf 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -99,7 +99,7 @@ var _ = AfterSuite(func() { Expect(c.Delete(ctx, operatorCatalog)).To(Succeed()) Eventually(func(g Gomega) { err := c.Get(ctx, types.NamespacedName{Name: operatorCatalog.Name}, &catalogd.Catalog{}) - Expect(errors.IsNotFound(err)).To(BeTrue()) + g.Expect(errors.IsNotFound(err)).To(BeTrue()) }).Should(Succeed()) // speed up delete without waiting for gc diff --git a/test/util/fake_catalog_client.go b/test/util/fake_catalog_client.go new file mode 100644 index 000000000..6177c9ff5 --- /dev/null +++ b/test/util/fake_catalog_client.go @@ -0,0 +1,31 @@ +package test_util + +import ( + "context" + + "github.com/operator-framework/operator-controller/internal/catalogmetadata" +) + +type FakeCatalogClient struct { + bundles []*catalogmetadata.Bundle + err error +} + +func NewFakeCatalogClient(b []*catalogmetadata.Bundle) FakeCatalogClient { + return FakeCatalogClient{ + bundles: b, + } +} + +func NewFakeCatalogClientWithError(e error) FakeCatalogClient { + return FakeCatalogClient{ + err: e, + } +} + +func (c *FakeCatalogClient) Bundles(ctx context.Context) ([]*catalogmetadata.Bundle, error) { + if c.err != nil { + return nil, c.err + } + return c.bundles, nil +}