Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
Signed-off-by: Fabrizio Sestito <fabrizio.sestito@suse.com>
  • Loading branch information
fabriziosestito committed Oct 11, 2024
1 parent 8ad8d48 commit a3744dc
Show file tree
Hide file tree
Showing 24 changed files with 1,129 additions and 61 deletions.
2 changes: 2 additions & 0 deletions api/storage/v1alpha1/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&ScanResult{},
&ScanResultList{},
&SBOM{},
&SBOMList{},
)
return nil
}
54 changes: 54 additions & 0 deletions api/storage/v1alpha1/sbom_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// SBOMList contains a list of Software Bill of Materials
type SBOMList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ScanResult `json:"items"`
}

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// SBOM represents a Software Bill of Materials of an OCI artifact
type SBOM struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec SBOMSpec `json:"spec,omitempty"`
Status SBOMStatus `json:"status,omitempty"`
}

// SBOMSpec defines the desired state of a SBOM
type SBOMSpec struct {
Data runtime.RawExtension `json:"data"`
}

// SBOMStatus defines the observed state of a SBOM
type SBOMStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
File renamed without changes.
94 changes: 94 additions & 0 deletions api/storage/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"

storagev1alpha1 "github.com/rancher/sbombastic/api/storage/v1alpha1"
"github.com/rancher/sbombastic/api/v1alpha1"
"github.com/rancher/sbombastic/internal/controller"
"github.com/rancher/sbombastic/internal/messaging"
Expand All @@ -48,8 +49,9 @@ var (

func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))

utilruntime.Must(v1alpha1.AddToScheme(scheme))
utilruntime.Must(storagev1alpha1.AddToScheme(scheme))

// +kubebuilder:scaffold:scheme
}

Expand Down Expand Up @@ -167,6 +169,16 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "Registry")
os.Exit(1)
}

if err = (&controller.ImageReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Publisher: publisher,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Image")
os.Exit(1)
}

// +kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Expand Down
56 changes: 41 additions & 15 deletions internal/controller/image_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,71 @@ package controller

import (
"context"
"fmt"

apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"

sbombasticv1alpha1 "github.com/rancher/sbombastic/api/v1alpha1"
storagev1alpha1 "github.com/rancher/sbombastic/api/storage/v1alpha1"
"github.com/rancher/sbombastic/api/v1alpha1"
"github.com/rancher/sbombastic/internal/messaging"
)

// ImageReconciler reconciles a Image object
type ImageReconciler struct {
client.Client
Scheme *runtime.Scheme
Scheme *runtime.Scheme
Publisher messaging.Publisher
}

// +kubebuilder:rbac:groups=sbombastic.sbombastic.rancher.io,resources=images,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=sbombastic.sbombastic.rancher.io,resources=images/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=sbombastic.sbombastic.rancher.io,resources=images/finalizers,verbs=update

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Image object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile
func (r *ImageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
log := log.FromContext(ctx)

// TODO(user): your logic here
var image v1alpha1.Image
if err := r.Get(ctx, req.NamespacedName, &image); err != nil {
if !apierrors.IsNotFound(err) {
return ctrl.Result{}, fmt.Errorf("unable to fetch Image: %w", err)
}

return ctrl.Result{}, nil
}

var sbom storagev1alpha1.SBOM
if err := r.Get(ctx, req.NamespacedName, &sbom); err != nil {
if apierrors.IsNotFound(err) {
log.Info("Creating SBOM of Image", "name", image.Name, "namespace", image.Namespace)

msg := messaging.CreateSBOM{
ImageName: image.Name,
ImageNamespace: image.Namespace,
}

if err := r.Publisher.Publish(&msg); err != nil {
return ctrl.Result{}, fmt.Errorf("unable to publish CreateSBOM message: %w", err)
}
}

return ctrl.Result{}, fmt.Errorf("unable to fetch SBOM: %w", err)
}

return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *ImageReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&sbombasticv1alpha1.Image{}).
err := ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.Image{}).
Complete(r)
if err != nil {
return fmt.Errorf("failed to create Image controller: %w", err)
}

return nil
}
81 changes: 36 additions & 45 deletions internal/controller/image_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,66 +19,57 @@ package controller
import (
"context"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
. "github.com/onsi/ginkgo/v2" //nolint:revive // Required for testing
. "github.com/onsi/gomega" //nolint:revive // Required for testing

"github.com/google/uuid"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

sbombasticv1alpha1 "github.com/rancher/sbombastic/api/v1alpha1"
"github.com/rancher/sbombastic/internal/messaging"
messagingMocks "github.com/rancher/sbombastic/internal/messaging/mocks"
)

var _ = Describe("Image Controller", func() {
Context("When reconciling a resource", func() {
const resourceName = "test-resource"

ctx := context.Background()
When("An Image without a SBOM is created", func() {
var reconciler ImageReconciler
var image sbombasticv1alpha1.Image

typeNamespacedName := types.NamespacedName{
Name: resourceName,
Namespace: "default", // TODO(user):Modify as needed
}
image := &sbombasticv1alpha1.Image{}
BeforeEach(func(ctx context.Context) {
By("Creating a new RegistryReconciler")
reconciler = ImageReconciler{
Client: k8sClient,
}

BeforeEach(func() {
By("creating the custom resource for the Kind Image")
err := k8sClient.Get(ctx, typeNamespacedName, image)
if err != nil && errors.IsNotFound(err) {
resource := &sbombasticv1alpha1.Image{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
Namespace: "default",
},
// TODO(user): Specify other spec details if needed.
}
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
By("Creating the Image")
image = sbombasticv1alpha1.Image{
ObjectMeta: metav1.ObjectMeta{
Name: uuid.New().String(),
Namespace: "default",
},
}
Expect(k8sClient.Create(ctx, &image)).To(Succeed())
})

AfterEach(func() {
// TODO(user): Cleanup logic after each test, like removing the resource instance.
resource := &sbombasticv1alpha1.Image{}
err := k8sClient.Get(ctx, typeNamespacedName, resource)
Expect(err).NotTo(HaveOccurred())

By("Cleanup the specific resource instance Image")
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
})
It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
controllerReconciler := &ImageReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}
It("should successfully reconcile the resource", func(ctx context.Context) {
By("Ensuring the right message is published to the worker queue")
mockPublisher := messagingMocks.NewPublisher(GinkgoT())
mockPublisher.On("Publish", &messaging.CreateSBOM{
ImageName: image.Name,
ImageNamespace: image.Namespace,
}).Return(nil)
reconciler.Publisher = mockPublisher

_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
By("Reconciling the Registry")
_, err := reconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: image.Name,
Namespace: image.Namespace,
},
})
Expect(err).NotTo(HaveOccurred())
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// Example: If you expect a certain status condition after reconciliation, verify it here.
})
})
})
Loading

0 comments on commit a3744dc

Please sign in to comment.