Skip to content

Add v1beta2 changes to IBMPowerVSImage #2412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/v1beta1/ibmpowervs_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,7 @@ func Convert_v1beta2_IBMPowerVSMachineStatus_To_v1beta1_IBMPowerVSMachineStatus(
// CAPI V1Beta2 was added in CAPIBM v1beta2
return autoConvert_v1beta2_IBMPowerVSMachineStatus_To_v1beta1_IBMPowerVSMachineStatus(in, out, s)
}

func Convert_v1beta2_IBMPowerVSImageStatus_To_v1beta1_IBMPowerVSImageStatus(in *infrav1.IBMPowerVSImageStatus, out *IBMPowerVSImageStatus, s apiconversion.Scope) error {
return autoConvert_v1beta2_IBMPowerVSImageStatus_To_v1beta1_IBMPowerVSImageStatus(in, out, s)
}
16 changes: 6 additions & 10 deletions api/v1beta1/zz_generated.conversion.go

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

27 changes: 26 additions & 1 deletion api/v1beta2/conditions_consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,17 @@ const (
)

const (
// ImageNotReadyReason used when the image is in a queued state.
// ImageNotReadyReason used when the image is not ready.
ImageNotReadyReason = "ImageNotReady"

// ImageImportFailedReason used when the image import is failed.
ImageImportFailedReason = "ImageImportFailed"

// ImageReconciliationFailedReason used when an error occurs during VPC Custom Image reconciliation.
ImageReconciliationFailedReason = "ImageReconciliationFailed"

// ImageQueuedReason used when the image is in queued state.
ImageQueuedReason = "ImageQueued"
)

const (
Expand All @@ -126,6 +129,9 @@ const (

// ImageImportedCondition reports on current status of the image import job. Ready indicates the import job is finished.
ImageImportedCondition clusterv1beta1.ConditionType = "ImageImported"

// IBMPowerVSImageDeletingV1Beta2Reason surfaces when the image is in deleting state.
IBMPowerVSImageDeletingV1Beta2Reason = clusterv1beta1.DeletingV1Beta2Reason
)

const (
Expand Down Expand Up @@ -295,3 +301,22 @@ const (
// COSInstanceDeletingV1Beta2Reason surfaces when the COS instance is being deleted.
COSInstanceDeletingV1Beta2Reason = clusterv1beta1.DeletingV1Beta2Reason
)

// IBMPowerVSImage's Ready condition and corresponding reasons that will be used in v1Beta2 API version.
const (
// IBMPowerVSImageReadyCondition is true if the IBMPowerVSImage's deletionTimestamp is not set, IBMPowerVSImage's.
IBMPowerVSImageReadyCondition = clusterv1beta1.ReadyV1Beta2Condition

// IBMPowerVSImageReadyV1Beta2Condition documents the Ready status of the image.
IBMPowerVSImageReadyV1Beta2Condition = "ImageReady"

// IBMPowerVSImageReadyV1Beta2Reason surfaces when the IBMPowerVSImage readiness criteria is met.
IBMPowerVSImageReadyV1Beta2Reason = clusterv1beta1.ReadyV1Beta2Reason

// IBMPowerVSImageNotReadyV1Beta2Reason surfaces when the IBMPowerVSImage readiness criteria is not met.
IBMPowerVSImageNotReadyV1Beta2Reason = clusterv1beta1.NotReadyV1Beta2Reason

// IBMPowerVSImageReadyUnknownV1Beta2Reason surfaces when at least one of the IBMPowerVSImage readiness criteria is unknown
// and none of the IBMPowerVSImage readiness criteria is met.
IBMPowerVSImageReadyUnknownV1Beta2Reason = clusterv1beta1.ReadyUnknownV1Beta2Reason
)
31 changes: 31 additions & 0 deletions api/v1beta2/ibmpowervsimage_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,21 @@ type IBMPowerVSImageStatus struct {
// Conditions defines current service state of the IBMPowerVSImage.
// +optional
Conditions clusterv1beta1.Conditions `json:"conditions,omitempty"`

// v1beta2 groups all the fields that will be added or modified in IBMPowerVSCluster's status with the V1Beta2 version.
// +optional
V1Beta2 *IBMPowerVSImageV1Beta2Status `json:"v1beta2,omitempty"`
}

// IBMPowerVSImageV1Beta2Status groups all the fields that will be added or modified in IBMPowerVSCluster with the V1Beta2 version.
// See https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20240916-improve-status-in-CAPI-resources.md for more context.
type IBMPowerVSImageV1Beta2Status struct {
// conditions represents the observations of a DevCluster's current state.
// +optional
// +listType=map
// +listMapKey=type
// +kubebuilder:validation:MaxItems=32
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

//+kubebuilder:object:root=true
Expand Down Expand Up @@ -121,6 +136,22 @@ func (r *IBMPowerVSImage) SetConditions(conditions clusterv1beta1.Conditions) {
r.Status.Conditions = conditions
}

// GetV1Beta2Conditions returns the set of conditions for this object.
func (r *IBMPowerVSImage) GetV1Beta2Conditions() []metav1.Condition {
if r.Status.V1Beta2 == nil {
return nil
}
return r.Status.V1Beta2.Conditions
}

// SetV1Beta2Conditions sets conditions for an API object.
func (r *IBMPowerVSImage) SetV1Beta2Conditions(conditions []metav1.Condition) {
if r.Status.V1Beta2 == nil {
r.Status.V1Beta2 = &IBMPowerVSImageV1Beta2Status{}
}
r.Status.V1Beta2.Conditions = conditions
}

//+kubebuilder:object:root=true

// IBMPowerVSImageList contains a list of IBMPowerVSImage.
Expand Down
7 changes: 5 additions & 2 deletions api/v1beta2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,17 @@ var (
// PowerVSImageStateACTIVE is the string representing an image in a active state.
PowerVSImageStateACTIVE = PowerVSImageState("active")

// PowerVSImageStateQue is the string representing an image in a queued state.
PowerVSImageStateQue = PowerVSImageState("queued")
// PowerVSImageStateQueued is the string representing an image in a queued state.
PowerVSImageStateQueued = PowerVSImageState("queued")

// PowerVSImageStateFailed is the string representing an image in a failed state.
PowerVSImageStateFailed = PowerVSImageState("failed")

// PowerVSImageStateImporting is the string representing an image in a failed state.
PowerVSImageStateImporting = PowerVSImageState("importing")

// PowerVSImageStateCompleted is the string representing an image in a completed state.
PowerVSImageStateCompleted = PowerVSImageState("completed")
)

// ServiceInstanceState describes the state of a service instance.
Expand Down
27 changes: 27 additions & 0 deletions api/v1beta2/zz_generated.deepcopy.go

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

72 changes: 22 additions & 50 deletions cloud/scope/powervs_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,14 @@ import (
"errors"
"fmt"

"github.com/go-logr/logr"

"github.com/IBM-Cloud/power-go-client/ibmpisession"
"github.com/IBM-Cloud/power-go-client/power/models"
"github.com/IBM/go-sdk-core/v5/core"
"github.com/IBM/platform-services-go-sdk/resourcecontrollerv2"

"k8s.io/klog/v2"

ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

v1beta1patch "sigs.k8s.io/cluster-api/util/deprecated/v1beta1/patch" //nolint:staticcheck

infrav1 "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta2"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/powervs"
"sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/resourcecontroller"
Expand All @@ -47,25 +42,22 @@ const BucketAccess = "public"
// PowerVSImageScopeParams defines the input parameters used to create a new PowerVSImageScope.
type PowerVSImageScopeParams struct {
Client client.Client
Logger logr.Logger
IBMPowerVSImage *infrav1.IBMPowerVSImage
ServiceEndpoint []endpoints.ServiceEndpoint
Zone *string
}

// PowerVSImageScope defines a scope defined around a Power VS Cluster.
type PowerVSImageScope struct {
logr.Logger
Client client.Client
patchHelper *v1beta1patch.Helper

Client client.Client
IBMPowerVSClient powervs.PowerVS
IBMPowerVSImage *infrav1.IBMPowerVSImage
ServiceEndpoint []endpoints.ServiceEndpoint
}

// NewPowerVSImageScope creates a new PowerVSImageScope from the supplied parameters.
func NewPowerVSImageScope(params PowerVSImageScopeParams) (scope *PowerVSImageScope, err error) {
func NewPowerVSImageScope(ctx context.Context, params PowerVSImageScopeParams) (scope *PowerVSImageScope, err error) {
log := ctrl.LoggerFrom(ctx)
scope = &PowerVSImageScope{}

if params.Client == nil {
Expand All @@ -80,25 +72,13 @@ func NewPowerVSImageScope(params PowerVSImageScopeParams) (scope *PowerVSImageSc
}
scope.IBMPowerVSImage = params.IBMPowerVSImage

if params.Logger == (logr.Logger{}) {
params.Logger = klog.Background()
}
scope.Logger = params.Logger

helper, err := v1beta1patch.NewHelper(params.IBMPowerVSImage, params.Client)
if err != nil {
err = fmt.Errorf("failed to init patch helper: %w", err)
return nil, err
}
scope.patchHelper = helper

// Create Resource Controller client.
var serviceOption resourcecontroller.ServiceOptions
// Fetch the resource controller endpoint.
rcEndpoint := endpoints.FetchEndpoints(string(endpoints.RC), params.ServiceEndpoint)
if rcEndpoint != "" {
serviceOption.URL = rcEndpoint
params.Logger.V(3).Info("Overriding the default resource controller endpoint", "ResourceControllerEndpoint", rcEndpoint)
log.V(3).Info("Overriding the default resource controller endpoint", "ResourceControllerEndpoint", rcEndpoint)
}

rc, err := resourcecontroller.NewService(serviceOption)
Expand All @@ -119,14 +99,15 @@ func NewPowerVSImageScope(params PowerVSImageScopeParams) (scope *PowerVSImageSc
}
serviceInstance, err := rc.GetServiceInstance("", name, params.Zone)
if err != nil {
params.Logger.Error(err, "error failed to get service instance id from name", "name", name)
log.Error(err, "error failed to get service instance id from name", "name", name)
return nil, err
}
if serviceInstance == nil {
return nil, fmt.Errorf("service instance %s is not yet created", name)
}
if *serviceInstance.State != string(infrav1.ServiceInstanceStateActive) {
return nil, fmt.Errorf("service instance %s is not in active state", name)
err = errors.New("service instance is not in active state")
return nil, err
}
serviceInstanceID = *serviceInstance.GUID
}
Expand All @@ -142,15 +123,15 @@ func NewPowerVSImageScope(params PowerVSImageScopeParams) (scope *PowerVSImageSc

options := powervs.ServiceOptions{
IBMPIOptions: &ibmpisession.IBMPIOptions{
Debug: params.Logger.V(DEBUGLEVEL).Enabled(),
Debug: log.V(DEBUGLEVEL).Enabled(),
Zone: *res.RegionID,
},
}

// Fetch the service endpoint.
if svcEndpoint := endpoints.FetchPVSEndpoint(endpoints.ConstructRegionFromZone(*res.RegionID), params.ServiceEndpoint); svcEndpoint != "" {
options.IBMPIOptions.URL = svcEndpoint
scope.Logger.V(3).Info("overriding the default powervs service endpoint")
log.V(3).Info("overriding the default powervs service endpoint", "serviceEndpoint", svcEndpoint)
}

c, err := powervs.NewService(options)
Expand Down Expand Up @@ -179,56 +160,47 @@ func (i *PowerVSImageScope) ensureImageUnique(imageName string) (*models.ImageRe
}

// CreateImageCOSBucket creates a power vs image.
func (i *PowerVSImageScope) CreateImageCOSBucket() (*models.ImageReference, *models.JobReference, error) {
s := i.IBMPowerVSImage.Spec
func (i *PowerVSImageScope) CreateImageCOSBucket(ctx context.Context) (*models.ImageReference, *models.JobReference, error) {
log := ctrl.LoggerFrom(ctx)
imageSpec := i.IBMPowerVSImage.Spec
m := i.IBMPowerVSImage.ObjectMeta

imageReply, err := i.ensureImageUnique(m.Name)
if err != nil {
record.Warnf(i.IBMPowerVSImage, "FailedRetrieveImage", "Failed to retrieve image %q", m.Name)
return nil, nil, err
} else if imageReply != nil {
i.Info("Image already exists")
log.Info("Image already exists", "imageName", m.Name)
return imageReply, nil, nil
}

if lastJob, _ := i.GetImportJob(); lastJob != nil {
if *lastJob.Status.State != "completed" && *lastJob.Status.State != "failed" {
i.Info("Previous import job not yet finished", "state", *lastJob.Status.State)
if *lastJob.Status.State != string(infrav1.PowerVSImageStateCompleted) && *lastJob.Status.State != string(infrav1.PowerVSImageStateFailed) {
log.Info("Previous import job not yet finished", "state", *lastJob.Status.State)
return nil, nil, nil
}
}

body := &models.CreateCosImageImportJob{
ImageName: &m.Name,
BucketName: s.Bucket,
BucketName: imageSpec.Bucket,
BucketAccess: core.StringPtr(BucketAccess),
Region: s.Region,
ImageFilename: s.Object,
StorageType: s.StorageType,
Region: imageSpec.Region,
ImageFilename: imageSpec.Object,
StorageType: imageSpec.StorageType,
}

jobRef, err := i.IBMPowerVSClient.CreateCosImage(body)
if err != nil {
i.Info("Unable to create new import job request")
log.Info("Unable to create new import job request")
record.Warnf(i.IBMPowerVSImage, "FailedCreateImageImportJob", "Failed image import job creation - %v", err)
return nil, nil, err
}
i.Info("New import job request created")
log.Info("New import job request created", "jobID", *jobRef.ID)
record.Eventf(i.IBMPowerVSImage, "SuccessfulCreateImageImportJob", "Created image import job %q", *jobRef.ID)
return nil, jobRef, nil
}

// PatchObject persists the cluster configuration and status.
func (i *PowerVSImageScope) PatchObject() error {
return i.patchHelper.Patch(context.TODO(), i.IBMPowerVSImage)
}

// Close closes the current scope persisting the cluster configuration and status.
func (i *PowerVSImageScope) Close() error {
return i.PatchObject()
}

// DeleteImage will delete the image.
func (i *PowerVSImageScope) DeleteImage() error {
if err := i.IBMPowerVSClient.DeleteImage(i.IBMPowerVSImage.Status.ImageID); err != nil {
Expand Down
Loading