Skip to content

Commit

Permalink
oran: add new oran package and provisioning request builder
Browse files Browse the repository at this point in the history
This PR adds the oran package and an initial builder for the ProvisioningRequest resource, along with unit tests.

Since this requires k8s.io/client-go 0.31, it cannot be merged for the time being.
  • Loading branch information
klaskosk committed Jan 10, 2025
1 parent 413fce0 commit bf81221
Show file tree
Hide file tree
Showing 9 changed files with 1,303 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ require (
github.com/openshift-kni/cluster-group-upgrades-operator v0.0.0-20240918194836-a0a3f896c283 // release-4.17
github.com/openshift-kni/lifecycle-agent v0.0.0-20241002223755-1e32b456449f // release-4.17
github.com/openshift-kni/numaresources-operator v0.4.17-0rc0.0.20240903095616-58342d895734 // release-4.17
github.com/openshift-kni/oran-o2ims/api/provisioning v0.0.0-20241112205445-5b445634c20c
github.com/openshift/api v3.9.1-0.20191111211345-a27ff30ebf09+incompatible
github.com/openshift/client-go v0.0.0-20240523113335-452272e0496d
github.com/openshift/cluster-logging-operator v0.0.0-20241003210634-afb65cea19d1 // release-5.9
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,8 @@ github.com/openshift-kni/lifecycle-agent v0.0.0-20241002223755-1e32b456449f h1:2
github.com/openshift-kni/lifecycle-agent v0.0.0-20241002223755-1e32b456449f/go.mod h1:HfcYUjKnuReNB0oE0tRJ8V9L699XepjfxXso+6IMZLk=
github.com/openshift-kni/numaresources-operator v0.4.17-0rc0.0.20240903095616-58342d895734 h1:12QyWN7/6+YszdioTgIDFeJY9nCxSqKGB9P9Pp2huhU=
github.com/openshift-kni/numaresources-operator v0.4.17-0rc0.0.20240903095616-58342d895734/go.mod h1:3JEkNx0nBloibpk6cxx5NDz3qPRRgJ03fq23plPEkAI=
github.com/openshift-kni/oran-o2ims/api/provisioning v0.0.0-20241112205445-5b445634c20c h1:sY1bBoM/6mdukAcS2YMyw2hFc9eveidB+yxOCJroBU0=
github.com/openshift-kni/oran-o2ims/api/provisioning v0.0.0-20241112205445-5b445634c20c/go.mod h1:u4yCIhhGsut5bXKOoP8jkOYUv8T3hjOb0RCpZR3+VbQ=
github.com/openshift/api v0.0.0-20240830023148-b7d0481c9094 h1:J1wuGhVxpsHykZBa6Beb1gQ96Ptej9AE/BvwCBiRj1E=
github.com/openshift/api v0.0.0-20240830023148-b7d0481c9094/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4=
github.com/openshift/client-go v0.0.0-20240523113335-452272e0496d h1:Dq21KMlDTVy2fCIyp0gsW+6ir6FwD3RjnCuza2/bIyM=
Expand Down
274 changes: 274 additions & 0 deletions pkg/oran/provisioningrequest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
package oran

import (
"context"
"fmt"

"github.com/golang/glog"
"github.com/openshift-kni/eco-goinfra/pkg/clients"
"github.com/openshift-kni/eco-goinfra/pkg/msg"
provisioningv1alpha1 "github.com/openshift-kni/oran-o2ims/api/provisioning/v1alpha1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
)

// ProvisioningRequestBuilder provides a struct to inferface with ProvisioningRequest resources on a specific cluster.
type ProvisioningRequestBuilder struct {
// Definition of the ProvisioningRequest used to create the resource.
Definition *provisioningv1alpha1.ProvisioningRequest
// Object of the ProvisioningRequest as it is on the cluster.
Object *provisioningv1alpha1.ProvisioningRequest
// apiClient used to interact with the cluster.
apiClient runtimeclient.Client
// errorMsg used to store latest error message from functions that do not return errors.
errorMsg string
}

// NewPRBuilder creates a new instance of a ProvisioningRequest builder.
func NewPRBuilder(
apiClient *clients.Settings, name, templateName, templateVersion string) *ProvisioningRequestBuilder {
glog.V(100).Infof(
"Initializing new ProvisioningRequest structure with the following params: "+
"name: %s, templateName: %s, templateVersion: %s",
name, templateName, templateVersion)

if apiClient == nil {
glog.V(100).Infof("The apiClient of the ProvisioningRequest is nil")

return nil
}

err := apiClient.AttachScheme(provisioningv1alpha1.AddToScheme)
if err != nil {
glog.V(100).Infof("Failed to add provisioning v1alpha1 scheme to client schemes: %v", err)

return nil
}

builder := &ProvisioningRequestBuilder{
apiClient: apiClient.Client,
Definition: &provisioningv1alpha1.ProvisioningRequest{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: provisioningv1alpha1.ProvisioningRequestSpec{
TemplateName: templateName,
TemplateVersion: templateVersion,
},
},
}

if name == "" {
glog.V(100).Info("The name of the ProvisioningRequest is empty")

builder.errorMsg = "provisioningRequest 'name' cannot be empty"

return builder
}

if templateName == "" {
glog.V(100).Info("The template name of the ProvisioningRequest is empty")

builder.errorMsg = "provisioningRequest 'templateName' cannot be empty"

return builder
}

if templateVersion == "" {
glog.V(100).Info("The template version of the ProvisioningRequest is empty")

builder.errorMsg = "provisioningRequest 'templateVersion' cannot be empty"

return builder
}

return builder
}

// PullPR pulls an existing ProvisioningRequest into a Builder struct.
func PullPR(apiClient *clients.Settings, name string) (*ProvisioningRequestBuilder, error) {
glog.V(100).Infof("Pulling existing ProvisioningRequest %s from cluster", name)

if apiClient == nil {
glog.V(100).Infof("The apiClient of the ProvisioningRequest is nil")

return nil, fmt.Errorf("provisioningRequest 'apiClient' cannot be nil")
}

err := apiClient.AttachScheme(provisioningv1alpha1.AddToScheme)
if err != nil {
glog.V(100).Infof("Failed to add provisioning v1alpha1 scheme to client schemes: %v", err)

return nil, err
}

builder := &ProvisioningRequestBuilder{
apiClient: apiClient.Client,
Definition: &provisioningv1alpha1.ProvisioningRequest{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
},
}

if name == "" {
glog.V(100).Info("The name of the ProvisioningRequest is empty")

return nil, fmt.Errorf("provisioningRequest 'name' cannot be empty")
}

if !builder.Exists() {
glog.V(100).Info("The ProvisioningRequest %s does not exist", name)

return nil, fmt.Errorf("provisioningRequest object %s does not exist", name)
}

builder.Definition = builder.Object

return builder, nil
}

// Get returns the ProvisioningRequest object if found.
func (builder *ProvisioningRequestBuilder) Get() (*provisioningv1alpha1.ProvisioningRequest, error) {
if valid, err := builder.validate(); !valid {
return nil, err
}

glog.V(100).Infof(
"Getting ProvisioningRequest object %s", builder.Definition.Name)

provisioningRequest := &provisioningv1alpha1.ProvisioningRequest{}
err := builder.apiClient.Get(context.TODO(), runtimeclient.ObjectKey{
Name: builder.Definition.Name,
}, provisioningRequest)

if err != nil {
glog.V(100).Infof("Failed to get ProvisioningRequest object %s: %v", builder.Definition.Name, err)

return nil, err
}

return provisioningRequest, nil
}

// Exists checks whether the given ProvisioningRequest exists on the cluster.
func (builder *ProvisioningRequestBuilder) Exists() bool {
if valid, _ := builder.validate(); !valid {
return false
}

glog.V(100).Infof("Checking if ProvisioningRequest %s exists", builder.Definition.Name)

var err error
builder.Object, err = builder.Get()

return err == nil || !k8serrors.IsNotFound(err)
}

// Create makes a ProvisioningRequest on the cluster if it does not already exist.
func (builder *ProvisioningRequestBuilder) Create() (*ProvisioningRequestBuilder, error) {
if valid, err := builder.validate(); !valid {
return nil, err
}

glog.V(100).Infof("Creating ProvisioningRequest %s", builder.Definition.Name)

if builder.Exists() {
return builder, nil
}

err := builder.apiClient.Create(context.TODO(), builder.Definition)
if err != nil {
return nil, err
}

builder.Object = builder.Definition

return builder, nil
}

// Update changes the existing ProvisioningRequest resource on the cluster. Since deleting a ProvisioningRequest is a
// non-trivial operation and corresponds to deleting a cluster, there is no option to fall back to deleting and
// recreating the ProvisioningRequest.
func (builder *ProvisioningRequestBuilder) Update() (*ProvisioningRequestBuilder, error) {
if valid, err := builder.validate(); !valid {
return nil, err
}

glog.V(100).Infof("Updating ProvisioningRequest %s", builder.Definition.Name)

if !builder.Exists() {
glog.V(100).Infof("ProvisioningRequest %s does not exist", builder.Definition.Name)

return nil, fmt.Errorf("cannot update non-existent provisioningRequest")
}

builder.Definition.ResourceVersion = builder.Object.ResourceVersion

err := builder.apiClient.Update(context.TODO(), builder.Definition)
if err != nil {
return nil, err
}

builder.Object = builder.Definition

return builder, nil
}

// Delete removes a ProvisioningRequest from the cluster if it exists.
func (builder *ProvisioningRequestBuilder) Delete() error {
if valid, err := builder.validate(); !valid {
return err
}

glog.V(100).Infof("Deleting ProvisioningRequest %s", builder.Definition.Name)

if !builder.Exists() {
glog.V(100).Infof("ProvisioningRequest %s does not exist", builder.Definition.Name)

builder.Object = nil

return nil
}

err := builder.apiClient.Delete(context.TODO(), builder.Object)
if err != nil {
return err
}

builder.Object = nil

return nil
}

// validate checks that the builder, definition, and apiClient are properly initialized and there is no errorMsg.
func (builder *ProvisioningRequestBuilder) validate() (bool, error) {
resourceCRD := "provisioningRequest"

if builder == nil {
glog.V(100).Infof("The %s builder is uninitialized", resourceCRD)

return false, fmt.Errorf("error: received nil %s builder", resourceCRD)
}

if builder.Definition == nil {
glog.V(100).Infof("The %s is uninitialized", resourceCRD)

return false, fmt.Errorf(msg.UndefinedCrdObjectErrString(resourceCRD))
}

if builder.apiClient == nil {
glog.V(100).Infof("The %s builder apiClient is nil", resourceCRD)

return false, fmt.Errorf("%s builder cannot have nil apiClient", resourceCRD)
}

if builder.errorMsg != "" {
glog.V(100).Infof("The %s builder has error message %s", resourceCRD, builder.errorMsg)

return false, fmt.Errorf(builder.errorMsg)
}

return true, nil
}
Loading

0 comments on commit bf81221

Please sign in to comment.