@@ -18,10 +18,14 @@ package controller
1818
1919import (
2020 "context"
21+ "errors"
2122 "fmt"
23+ "net/http"
2224 "testing"
2325 "time"
2426
27+ githubmocks "github.com/SovereignCloudStack/cluster-stack-operator/pkg/github/client/mocks"
28+ "github.com/google/go-github/v52/github"
2529 "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
2630 . "github.com/onsi/ginkgo/v2"
2731 . "github.com/onsi/gomega"
@@ -30,8 +34,10 @@ import (
3034 corev1 "k8s.io/api/core/v1"
3135 apierrors "k8s.io/apimachinery/pkg/api/errors"
3236 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
37+ "k8s.io/apimachinery/pkg/runtime"
3338 "k8s.io/apimachinery/pkg/types"
3439 capoapiv1alpha7 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7"
40+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
3541)
3642
3743const (
@@ -168,6 +174,305 @@ func TestGetNodeImagesFromLocal(t *testing.T) {
168174 })
169175}
170176
177+ func TestDownloadReleaseAssets (t * testing.T ) {
178+ ctx := context .TODO ()
179+ releaseTag := "v1.0.0"
180+ downloadPath := "/tmp/download"
181+ assetlist := []string {metadataFileName , nodeImagesFileName }
182+ mockGitHubClient := githubmocks .NewClient (t )
183+ mockHTTPResponse := & http.Response {
184+ StatusCode : http .StatusOK ,
185+ }
186+ mockResponse := & github.Response {
187+ Response : mockHTTPResponse ,
188+ }
189+ mockRepoRelease := & github.RepositoryRelease {
190+ Name : github .String ("test-release-name" ),
191+ }
192+
193+ mockGitHubClient .On ("GetReleaseByTag" , ctx , releaseTag ).Return (mockRepoRelease , mockResponse , nil )
194+ repoRelease , resp , err := mockGitHubClient .GetReleaseByTag (ctx , releaseTag )
195+ assert .NoError (t , err )
196+ assert .Equal (t , resp .StatusCode , http .StatusOK )
197+
198+ mockGitHubClient .On ("DownloadReleaseAssets" , ctx , repoRelease , downloadPath , assetlist ).Return (nil )
199+
200+ err = downloadReleaseAssets (ctx , releaseTag , downloadPath , mockGitHubClient )
201+
202+ assert .NoError (t , err )
203+ }
204+
205+ func TestDownloadReleaseAssetsFailedToDownload (t * testing.T ) {
206+ ctx := context .TODO ()
207+ releaseTag := "v1.0.0"
208+ mockGitHubClient := githubmocks .NewClient (t )
209+ downloadPath := "/tmp/download"
210+ assetlist := []string {metadataFileName , nodeImagesFileName }
211+ mockHTTPResponse := & http.Response {
212+ StatusCode : http .StatusOK ,
213+ }
214+ mockResponse := & github.Response {
215+ Response : mockHTTPResponse ,
216+ }
217+ mockRepoRelease := & github.RepositoryRelease {
218+ Name : github .String ("test-release-name" ),
219+ }
220+
221+ mockGitHubClient .On ("GetReleaseByTag" , ctx , releaseTag ).Return (mockRepoRelease , mockResponse , nil )
222+ repoRelease , resp , err := mockGitHubClient .GetReleaseByTag (ctx , releaseTag )
223+ assert .NoError (t , err )
224+ assert .Equal (t , resp .StatusCode , http .StatusOK )
225+
226+ mockGitHubClient .On ("DownloadReleaseAssets" , ctx , repoRelease , downloadPath , assetlist ).Return (errors .New ("failed to download release assets" ))
227+ err = downloadReleaseAssets (ctx , releaseTag , downloadPath , mockGitHubClient )
228+
229+ assert .ErrorContains (t , err , "failed to download release assets" )
230+ }
231+
232+ func TestGetOwnedOpenStackNodeImageReleases (t * testing.T ) {
233+ scheme := runtime .NewScheme ()
234+ err := apiv1alpha1 .AddToScheme (scheme )
235+ assert .NoError (t , err )
236+ client := fake .NewClientBuilder ().WithScheme (scheme ).Build ()
237+
238+ openstackclusterstackrelease := & apiv1alpha1.OpenStackClusterStackRelease {
239+ TypeMeta : metav1.TypeMeta {
240+ APIVersion : "clusterstack.x-k8s.io/v1alpha1" ,
241+ Kind : "OpenStackClusterStackRelease" ,
242+ },
243+ ObjectMeta : metav1.ObjectMeta {
244+ Name : "test-release" ,
245+ Namespace : "test-namespace" ,
246+ },
247+ Spec : apiv1alpha1.OpenStackClusterStackReleaseSpec {
248+ CloudName : "test-cloudname" ,
249+ IdentityRef : & capoapiv1alpha7.OpenStackIdentityReference {
250+ Kind : "Secret" ,
251+ Name : "supersecret" ,
252+ },
253+ },
254+ }
255+ assert .NoError (t , client .Create (context .TODO (), openstackclusterstackrelease ))
256+
257+ openstackNodeImageRelease := & apiv1alpha1.OpenStackNodeImageRelease {
258+ TypeMeta : metav1.TypeMeta {
259+ APIVersion : "clusterstack.x-k8s.io/v1alpha1" ,
260+ Kind : "OpenStackNodeImageRelease" ,
261+ },
262+ ObjectMeta : metav1.ObjectMeta {
263+ Name : "test-node-image-release" ,
264+ Namespace : "test-namespace" ,
265+ OwnerReferences : []metav1.OwnerReference {
266+ {
267+ APIVersion : openstackclusterstackrelease .APIVersion ,
268+ Kind : openstackclusterstackrelease .Kind ,
269+ Name : openstackclusterstackrelease .Name ,
270+ UID : openstackclusterstackrelease .UID ,
271+ },
272+ },
273+ },
274+ }
275+
276+ assert .NoError (t , client .Create (context .TODO (), openstackNodeImageRelease ))
277+
278+ r := & OpenStackClusterStackReleaseReconciler {
279+ Client : client ,
280+ }
281+
282+ ownedOpenStackNodeImageReleases , err := r .getOwnedOpenStackNodeImageReleases (context .TODO (), openstackclusterstackrelease )
283+
284+ assert .NoError (t , err )
285+ assert .NotEmpty (t , ownedOpenStackNodeImageReleases )
286+ assert .Contains (t , ownedOpenStackNodeImageReleases , openstackNodeImageRelease )
287+ }
288+
289+ func TestCreateOpenStackNodeImageRelease (t * testing.T ) {
290+ scheme := runtime .NewScheme ()
291+ err := apiv1alpha1 .AddToScheme (scheme )
292+ assert .NoError (t , err )
293+ client := fake .NewClientBuilder ().WithScheme (scheme ).Build ()
294+
295+ openstackclusterstackrelease := & apiv1alpha1.OpenStackClusterStackRelease {
296+ TypeMeta : metav1.TypeMeta {
297+ APIVersion : "clusterstack.x-k8s.io/v1alpha1" ,
298+ Kind : "OpenStackClusterStackRelease" ,
299+ },
300+ ObjectMeta : metav1.ObjectMeta {
301+ Name : "test-release" ,
302+ Namespace : "test-namespace" ,
303+ },
304+ Spec : apiv1alpha1.OpenStackClusterStackReleaseSpec {
305+ CloudName : "test-cloudname" ,
306+ IdentityRef : & capoapiv1alpha7.OpenStackIdentityReference {
307+ Kind : "Secret" ,
308+ Name : "supersecret" ,
309+ },
310+ },
311+ }
312+ assert .NoError (t , client .Create (context .TODO (), openstackclusterstackrelease ))
313+
314+ openStackNodeImage := & apiv1alpha1.OpenStackNodeImage {
315+ URL : "test-url" ,
316+ CreateOpts : & apiv1alpha1.CreateOpts {
317+ Name : "test-image" ,
318+ ID : "testID" ,
319+ ContainerFormat : "bare" ,
320+ DiskFormat : "qcow2" ,
321+ },
322+ }
323+
324+ ownerRef := & metav1.OwnerReference {
325+ APIVersion : openstackclusterstackrelease .APIVersion ,
326+ Kind : openstackclusterstackrelease .Kind ,
327+ Name : openstackclusterstackrelease .Name ,
328+ UID : openstackclusterstackrelease .UID ,
329+ }
330+
331+ r := & OpenStackClusterStackReleaseReconciler {
332+ Client : client ,
333+ }
334+
335+ err = r .createOrUpdateOpenStackNodeImageRelease (context .TODO (), openstackclusterstackrelease , "test-osnir" , openStackNodeImage , ownerRef )
336+
337+ assert .NoError (t , err )
338+ osnir := & apiv1alpha1.OpenStackNodeImageRelease {}
339+ err = client .Get (context .TODO (), types.NamespacedName {Name : "test-osnir" , Namespace : "test-namespace" }, osnir )
340+ assert .NoError (t , err )
341+ assert .NotNil (t , osnir )
342+
343+ expectedosnir := & apiv1alpha1.OpenStackNodeImageRelease {
344+ TypeMeta : metav1.TypeMeta {
345+ Kind : "OpenStackNodeImageRelease" ,
346+ APIVersion : apiv1alpha1 .GroupVersion .String (),
347+ },
348+ Spec : apiv1alpha1.OpenStackNodeImageReleaseSpec {
349+ CloudName : "test-cloudname" ,
350+ IdentityRef : & capoapiv1alpha7.OpenStackIdentityReference {
351+ Kind : "Secret" ,
352+ Name : "supersecret" ,
353+ },
354+ Image : & apiv1alpha1.OpenStackNodeImage {
355+ URL : "test-url" ,
356+ CreateOpts : & apiv1alpha1.CreateOpts {
357+ Name : "test-image" ,
358+ ID : "testID" ,
359+ ContainerFormat : "bare" ,
360+ DiskFormat : "qcow2" ,
361+ },
362+ },
363+ },
364+ }
365+ assert .Equal (t , expectedosnir .Spec , osnir .Spec )
366+ assert .Equal (t , expectedosnir .TypeMeta , osnir .TypeMeta )
367+
368+ // Test cleanup
369+ err = client .Delete (context .TODO (), osnir )
370+ assert .NoError (t , err )
371+ err = client .Delete (context .TODO (), openstackclusterstackrelease )
372+ assert .NoError (t , err )
373+ }
374+
375+ func TestUpdateOpenStackNodeImageRelease (t * testing.T ) {
376+ scheme := runtime .NewScheme ()
377+ err := apiv1alpha1 .AddToScheme (scheme )
378+ assert .NoError (t , err )
379+ client := fake .NewClientBuilder ().WithScheme (scheme ).Build ()
380+
381+ openstackclusterstackrelease := & apiv1alpha1.OpenStackClusterStackRelease {
382+ TypeMeta : metav1.TypeMeta {
383+ APIVersion : "clusterstack.x-k8s.io/v1alpha1" ,
384+ Kind : "OpenStackClusterStackRelease" ,
385+ },
386+ ObjectMeta : metav1.ObjectMeta {
387+ Name : "test-release" ,
388+ Namespace : "test-namespace" ,
389+ },
390+ Spec : apiv1alpha1.OpenStackClusterStackReleaseSpec {
391+ CloudName : "test-cloudname" ,
392+ IdentityRef : & capoapiv1alpha7.OpenStackIdentityReference {
393+ Kind : "Secret" ,
394+ Name : "supersecret" ,
395+ },
396+ },
397+ }
398+ assert .NoError (t , client .Create (context .TODO (), openstackclusterstackrelease ))
399+
400+ openStackNodeImage := & apiv1alpha1.OpenStackNodeImage {
401+ URL : "test-url" ,
402+ CreateOpts : & apiv1alpha1.CreateOpts {
403+ Name : "test-image" ,
404+ ID : "testID" ,
405+ ContainerFormat : "bare" ,
406+ DiskFormat : "qcow2" ,
407+ },
408+ }
409+
410+ ownerRef := & metav1.OwnerReference {
411+ APIVersion : openstackclusterstackrelease .APIVersion ,
412+ Kind : openstackclusterstackrelease .Kind ,
413+ Name : openstackclusterstackrelease .Name ,
414+ UID : "test-uid" ,
415+ }
416+
417+ r := & OpenStackClusterStackReleaseReconciler {
418+ Client : client ,
419+ }
420+
421+ osnir := & apiv1alpha1.OpenStackNodeImageRelease {
422+ TypeMeta : metav1.TypeMeta {
423+ Kind : "OpenStackNodeImageRelease" ,
424+ APIVersion : apiv1alpha1 .GroupVersion .String (),
425+ },
426+ ObjectMeta : metav1.ObjectMeta {
427+ Name : "test-update-osnir" ,
428+ Namespace : "test-namespace" ,
429+ },
430+ Spec : apiv1alpha1.OpenStackNodeImageReleaseSpec {
431+ CloudName : "test-cloudname" ,
432+ IdentityRef : & capoapiv1alpha7.OpenStackIdentityReference {
433+ Kind : "Secret" ,
434+ Name : "supersecret" ,
435+ },
436+ Image : & apiv1alpha1.OpenStackNodeImage {
437+ URL : "test-url" ,
438+ CreateOpts : & apiv1alpha1.CreateOpts {
439+ Name : "test-image" ,
440+ ID : "testID" ,
441+ ContainerFormat : "bare" ,
442+ DiskFormat : "qcow2" ,
443+ },
444+ },
445+ },
446+ }
447+ osnir .SetOwnerReferences ([]metav1.OwnerReference {* ownerRef })
448+ assert .NoError (t , client .Create (context .TODO (), osnir ))
449+
450+ newOwnerRef := & metav1.OwnerReference {
451+ APIVersion : openstackclusterstackrelease .APIVersion ,
452+ Kind : openstackclusterstackrelease .Kind ,
453+ Name : openstackclusterstackrelease .Name ,
454+ UID : "test-new-uid" ,
455+ }
456+
457+ err = client .Get (context .TODO (), types.NamespacedName {Name : "test-update-osnir" , Namespace : "test-namespace" }, & apiv1alpha1.OpenStackNodeImageRelease {})
458+ assert .NoError (t , err )
459+ assert .Equal (t , ownerRef .UID , osnir .OwnerReferences [0 ].UID )
460+
461+ err = r .createOrUpdateOpenStackNodeImageRelease (context .TODO (), openstackclusterstackrelease , "test-update-osnir" , openStackNodeImage , newOwnerRef )
462+ assert .NoError (t , err )
463+
464+ err = client .Get (context .TODO (), types.NamespacedName {Name : "test-update-osnir" , Namespace : "test-namespace" }, osnir )
465+ assert .NoError (t , err )
466+ assert .NotNil (t , osnir )
467+ assert .Equal (t , * newOwnerRef , osnir .OwnerReferences [0 ])
468+
469+ // Test cleanup
470+ err = client .Delete (context .TODO (), osnir )
471+ assert .NoError (t , err )
472+ err = client .Delete (context .TODO (), openstackclusterstackrelease )
473+ assert .NoError (t , err )
474+ }
475+
171476var _ = Describe ("OpenStackClusterStackRelease controller" , func () {
172477 Context ("OpenStackClusterStackRelease controller test" , func () {
173478 const openstackclusterstackreleasename = "test-ocsr"
0 commit comments