Skip to content
Merged
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: 2 additions & 2 deletions pkg/lint/check/condition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,14 +437,14 @@ func TestNewCondition_WithMultipleFormatArgs(t *testing.T) {
check.ConditionTypeCompatible,
metav1.ConditionFalse,
check.WithReason(check.ReasonVersionIncompatible),
check.WithMessage("Found %d %s - will be impacted in RHOAI %s", 3, "InferenceServices", "3.x"),
check.WithMessage("Found %d %s - will be impacted in RHOAI %s", 3, "InferenceServices", "3.0"),
)

g.Expect(condition.Condition).To(MatchFields(IgnoreExtras, Fields{
"Type": Equal(check.ConditionTypeCompatible),
"Status": Equal(metav1.ConditionFalse),
"Reason": Equal(check.ReasonVersionIncompatible),
"Message": Equal("Found 3 InferenceServices - will be impacted in RHOAI 3.x"),
"Message": Equal("Found 3 InferenceServices - will be impacted in RHOAI 3.0"),
}))
}

Expand Down
17 changes: 10 additions & 7 deletions pkg/lint/check/validate/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/opendatahub-io/odh-cli/pkg/lint/check/result"
"github.com/opendatahub-io/odh-cli/pkg/util/client"
"github.com/opendatahub-io/odh-cli/pkg/util/components"
"github.com/opendatahub-io/odh-cli/pkg/util/version"
)

// ComponentBuilder provides a fluent API for component-based validation.
Expand Down Expand Up @@ -50,7 +51,12 @@ func Component(c check.Check, target check.Target) *ComponentBuilder {
// ComponentRequest contains pre-fetched data for component validation.
// It provides convenient access to commonly needed data without requiring
// callbacks to parse annotations or fetch additional resources.
//
// check.Target is embedded, so fields like Client, TargetVersion, and CurrentVersion
// are directly accessible (e.g. req.Client, req.TargetVersion).
type ComponentRequest struct {
check.Target

// Result is the pre-created DiagnosticResult with auto-populated annotations.
Result *result.DiagnosticResult

Expand All @@ -60,9 +66,6 @@ type ComponentRequest struct {
// ManagementState is the component's management state string.
ManagementState string

// Client provides read-only access to the Kubernetes API.
Client client.Reader

// ApplicationsNamespace is populated when WithApplicationsNamespace() is used.
// Empty string if not requested. If DSCI is not found, Run() returns early
// with a "not found" diagnostic result before calling the validation function.
Expand Down Expand Up @@ -101,18 +104,18 @@ func (b *ComponentBuilder) WithApplicationsNamespace() *ComponentBuilder {
}

// Removal returns a ComponentValidateFn that sets a compatibility failure condition.
// ManagementState is automatically prepended as the first format argument.
// ManagementState and target version label are automatically supplied as the first two format arguments.
//
// Example:
//
// validate.Component(c, target).
// InState(constants.ManagementStateManaged).
// Run(ctx, validate.Removal("CodeFlare is enabled (state: %s) but will be removed in RHOAI 3.x"))
// Run(ctx, validate.Removal("CodeFlare is enabled (state: %s) but will be removed in RHOAI %s"))
func Removal(format string, opts ...check.ConditionOption) ComponentValidateFn {
return func(_ context.Context, req *ComponentRequest) error {
allOpts := append([]check.ConditionOption{
check.WithReason(check.ReasonVersionIncompatible),
check.WithMessage(format, req.ManagementState),
check.WithMessage(format, req.ManagementState, version.MajorMinorLabel(req.TargetVersion)),
}, opts...)
req.Result.SetCondition(check.NewCondition(
check.ConditionTypeCompatible,
Expand Down Expand Up @@ -195,10 +198,10 @@ func (b *ComponentBuilder) Run(

// Create the request with pre-populated data
req := &ComponentRequest{
Target: b.target,
Result: dr,
DSC: dsc,
ManagementState: state,
Client: b.target.Client,
}

// Load applications namespace if requested
Expand Down
23 changes: 6 additions & 17 deletions pkg/lint/check/validate/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,21 @@ import (
"github.com/opendatahub-io/odh-cli/pkg/lint/check/result"
"github.com/opendatahub-io/odh-cli/pkg/resources"
"github.com/opendatahub-io/odh-cli/pkg/util/client"
"github.com/opendatahub-io/odh-cli/pkg/util/iostreams"
"github.com/opendatahub-io/odh-cli/pkg/util/kube"
)

// WorkloadRequest contains the pre-fetched data passed to the workload validation function.
//
// check.Target is embedded, so fields like Client, IO, Debug, TargetVersion, and CurrentVersion
// are directly accessible (e.g. req.Client, req.IO, req.Debug, req.TargetVersion).
type WorkloadRequest[T any] struct {
check.Target

// Result is the pre-created DiagnosticResult with auto-populated annotations.
Result *result.DiagnosticResult

// Items contains the (optionally filtered) workload items.
Items []T

// Client provides read-only access to the Kubernetes API.
Client client.Reader

// IO provides access to input/output streams for verbose logging.
// Use IO.Errorf() for debug output that appears only when --verbose is set.
// May be nil if no IO was provided to the check target.
IO iostreams.Interface

// Debug indicates whether detailed diagnostic logging is enabled.
// When true, checks should emit internal processing logs for troubleshooting.
// When false, only user-facing summary information should be logged via IO.
Debug bool
}

// WorkloadValidateFn is the callback invoked by WorkloadBuilder.Run after listing and filtering.
Expand Down Expand Up @@ -144,11 +135,9 @@ func (b *WorkloadBuilder[T]) Run(

// Call the validation function.
req := &WorkloadRequest[T]{
Target: b.target,
Result: dr,
Items: items,
Client: b.target.Client,
IO: b.target.IO,
Debug: b.target.Debug,
}

if err := fn(ctx, req); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/lint/checks/components/codeflare/codeflare.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (c *RemovalCheck) CanApply(ctx context.Context, target check.Target) (bool,
// Validate executes the check against the provided target.
func (c *RemovalCheck) Validate(ctx context.Context, target check.Target) (*result.DiagnosticResult, error) {
return validate.Component(c, target).
Run(ctx, validate.Removal("CodeFlare is enabled (state: %s) but will be removed in RHOAI 3.x",
Run(ctx, validate.Removal("CodeFlare is enabled (state: %s) but will be removed in RHOAI %s",
check.WithImpact(result.ImpactBlocking),
check.WithRemediation(c.CheckRemediation)))
}
2 changes: 1 addition & 1 deletion pkg/lint/checks/components/codeflare/codeflare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func TestCodeFlareRemovalCheck_ManagedBlocking(t *testing.T) {
"Type": Equal(check.ConditionTypeCompatible),
"Status": Equal(metav1.ConditionFalse),
"Reason": Equal(check.ReasonVersionIncompatible),
"Message": And(ContainSubstring("enabled"), ContainSubstring("removed in RHOAI 3.x")),
"Message": And(ContainSubstring("enabled"), ContainSubstring("removed in RHOAI 3.0")),
}))
g.Expect(result.Status.Conditions[0].Impact).To(Equal(resultpkg.ImpactBlocking))
g.Expect(result.Annotations).To(And(
Expand Down
4 changes: 3 additions & 1 deletion pkg/lint/checks/components/datasciencepipelines/renaming.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,14 @@ func (c *RenamingCheck) Validate(ctx context.Context, target check.Target) (*res
}

func newRenamingCondition(_ context.Context, req *validate.ComponentRequest) ([]result.Condition, error) {
tv := version.MajorMinorLabel(req.TargetVersion)

return []result.Condition{
check.NewCondition(
check.ConditionTypeCompatible,
metav1.ConditionFalse,
check.WithReason(check.ReasonComponentRenamed),
check.WithMessage("DataSciencePipelines component (state: %s) will be renamed to AIPipelines in DSC v2 (RHOAI 3.x). The field path changes from '.spec.components.datasciencepipelines' to '.spec.components.aipipelines'", req.ManagementState),
check.WithMessage("DataSciencePipelines component (state: %s) will be renamed to AIPipelines in DSC v2 (RHOAI %s). The field path changes from '.spec.components.datasciencepipelines' to '.spec.components.aipipelines'", req.ManagementState, tv),
check.WithImpact(result.ImpactAdvisory),
check.WithRemediation("No action required - the component will be automatically renamed. Update any automation referencing '.spec.components.datasciencepipelines' to use '.spec.components.aipipelines' after upgrade"),
),
Expand Down
7 changes: 4 additions & 3 deletions pkg/lint/checks/components/kserve/serverless.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func (c *ServerlessRemovalCheck) CanApply(ctx context.Context, target check.Targ
func (c *ServerlessRemovalCheck) Validate(ctx context.Context, target check.Target) (*result.DiagnosticResult, error) {
return validate.Component(c, target).
Run(ctx, func(_ context.Context, req *validate.ComponentRequest) error {
tv := version.MajorMinorLabel(req.TargetVersion)
state, err := jq.Query[string](req.DSC, ".spec.components.kserve.serving.managementState")

switch {
Expand All @@ -64,7 +65,7 @@ func (c *ServerlessRemovalCheck) Validate(ctx context.Context, target check.Targ
check.ConditionTypeCompatible,
metav1.ConditionTrue,
check.WithReason(check.ReasonVersionCompatible),
check.WithMessage("KServe serverless mode is not configured - ready for RHOAI 3.x upgrade"),
check.WithMessage("KServe serverless mode is not configured - ready for RHOAI %s upgrade", tv),
))
case err != nil:
return fmt.Errorf("querying kserve serving managementState: %w", err)
Expand All @@ -73,7 +74,7 @@ func (c *ServerlessRemovalCheck) Validate(ctx context.Context, target check.Targ
check.ConditionTypeCompatible,
metav1.ConditionFalse,
check.WithReason(check.ReasonVersionIncompatible),
check.WithMessage("KServe serverless mode is enabled (state: %s) but will be removed in RHOAI 3.x", state),
check.WithMessage("KServe serverless mode is enabled (state: %s) but will be removed in RHOAI %s", state, tv),
check.WithImpact(result.ImpactBlocking),
check.WithRemediation(c.CheckRemediation),
))
Expand All @@ -82,7 +83,7 @@ func (c *ServerlessRemovalCheck) Validate(ctx context.Context, target check.Targ
check.ConditionTypeCompatible,
metav1.ConditionTrue,
check.WithReason(check.ReasonVersionCompatible),
check.WithMessage("KServe serverless mode is disabled (state: %s) - ready for RHOAI 3.x upgrade", state),
check.WithMessage("KServe serverless mode is disabled (state: %s) - ready for RHOAI %s upgrade", state, tv),
))
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/lint/checks/components/kserve/serverless_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func TestKServeServerlessRemovalCheck_ServerlessManagedBlocking(t *testing.T) {
"Type": Equal(check.ConditionTypeCompatible),
"Status": Equal(metav1.ConditionFalse),
"Reason": Equal(check.ReasonVersionIncompatible),
"Message": And(ContainSubstring("serverless mode is enabled"), ContainSubstring("removed in RHOAI 3.x")),
"Message": And(ContainSubstring("serverless mode is enabled"), ContainSubstring("removed in RHOAI 3.0")),
}))
g.Expect(result.Status.Conditions[0].Impact).To(Equal(resultpkg.ImpactBlocking))
g.Expect(result.Annotations).To(And(
Expand Down Expand Up @@ -253,7 +253,7 @@ func TestKServeServerlessRemovalCheck_ServerlessRemovedReady(t *testing.T) {
"Type": Equal(check.ConditionTypeCompatible),
"Status": Equal(metav1.ConditionTrue),
"Reason": Equal(check.ReasonVersionCompatible),
"Message": And(ContainSubstring("serverless mode is disabled"), ContainSubstring("ready for RHOAI 3.x upgrade")),
"Message": And(ContainSubstring("serverless mode is disabled"), ContainSubstring("ready for RHOAI 3.0 upgrade")),
}))
g.Expect(result.Annotations).To(HaveKeyWithValue(check.AnnotationComponentManagementState, constants.ManagementStateManaged))
}
Expand Down
6 changes: 4 additions & 2 deletions pkg/lint/checks/components/kueue/kueue.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,15 @@ func (c *ManagementStateCheck) CanApply(ctx context.Context, target check.Target
func (c *ManagementStateCheck) Validate(ctx context.Context, target check.Target) (*result.DiagnosticResult, error) {
return validate.Component(c, target).
Run(ctx, func(_ context.Context, req *validate.ComponentRequest) error {
tv := version.MajorMinorLabel(req.TargetVersion)

switch req.ManagementState {
case constants.ManagementStateManaged:
req.Result.SetCondition(check.NewCondition(
check.ConditionTypeCompatible,
metav1.ConditionFalse,
check.WithReason(check.ReasonVersionIncompatible),
check.WithMessage("Kueue is managed by OpenShift AI (state: %s) but Managed option will be removed in RHOAI 3.x", req.ManagementState),
check.WithMessage("Kueue is managed by OpenShift AI (state: %s) but Managed option will be removed in RHOAI %s", req.ManagementState, tv),
check.WithImpact(result.ImpactBlocking),
check.WithRemediation(c.CheckRemediation),
))
Expand All @@ -78,7 +80,7 @@ func (c *ManagementStateCheck) Validate(ctx context.Context, target check.Target
check.ConditionTypeCompatible,
metav1.ConditionTrue,
check.WithReason(check.ReasonVersionCompatible),
check.WithMessage("Kueue managementState is %s — compatible with RHOAI 3.x", req.ManagementState),
check.WithMessage("Kueue managementState is %s — compatible with RHOAI %s", req.ManagementState, tv),
))
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/lint/checks/components/kueue/kueue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func TestManagementStateCheck_UnmanagedAllowed(t *testing.T) {
"Type": Equal(check.ConditionTypeCompatible),
"Status": Equal(metav1.ConditionTrue),
"Reason": Equal(check.ReasonVersionCompatible),
"Message": ContainSubstring("compatible with RHOAI 3.x"),
"Message": ContainSubstring("compatible with RHOAI 3.1"),
}))
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/lint/checks/components/modelmesh/modelmesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (c *RemovalCheck) CanApply(ctx context.Context, target check.Target) (bool,

func (c *RemovalCheck) Validate(ctx context.Context, target check.Target) (*result.DiagnosticResult, error) {
return validate.Component(c, target).
Run(ctx, validate.Removal("ModelMesh is enabled (state: %s) but will be removed in RHOAI 3.x",
Run(ctx, validate.Removal("ModelMesh is enabled (state: %s) but will be removed in RHOAI %s",
check.WithImpact(result.ImpactBlocking),
check.WithRemediation(c.CheckRemediation)))
}
2 changes: 1 addition & 1 deletion pkg/lint/checks/components/modelmesh/modelmesh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func TestModelmeshRemovalCheck_ManagedBlocking(t *testing.T) {
"Type": Equal(check.ConditionTypeCompatible),
"Status": Equal(metav1.ConditionFalse),
"Reason": Equal(check.ReasonVersionIncompatible),
"Message": And(ContainSubstring("enabled"), ContainSubstring("removed in RHOAI 3.x")),
"Message": And(ContainSubstring("enabled"), ContainSubstring("removed in RHOAI 3.0")),
}))
g.Expect(result.Status.Conditions[0].Impact).To(Equal(resultpkg.ImpactBlocking))
g.Expect(result.Annotations).To(And(
Expand Down
9 changes: 5 additions & 4 deletions pkg/lint/checks/dependencies/openshift/openshift.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func (c *Check) Validate(
target check.Target,
) (*result.DiagnosticResult, error) {
dr := c.NewResult()
tv := version.MajorMinorLabel(target.TargetVersion)

ver, err := version.DetectOpenShiftVersion(ctx, target.Client)

Expand All @@ -57,23 +58,23 @@ func (c *Check) Validate(
check.ConditionTypeCompatible,
metav1.ConditionFalse,
check.WithReason(check.ReasonInsufficientData),
check.WithMessage("Unable to detect OpenShift version: %s. RHOAI 3.x requires OpenShift %s or later", err.Error(), minVersion.String()),
check.WithMessage("Unable to detect OpenShift version: %s. RHOAI %s requires OpenShift %s or later", err.Error(), tv, minVersion.String()),
check.WithImpact(result.ImpactBlocking),
))
case ver.GTE(minVersion):
dr.SetCondition(check.NewCondition(
check.ConditionTypeCompatible,
metav1.ConditionTrue,
check.WithReason(check.ReasonVersionCompatible),
check.WithMessage("OpenShift %s meets RHOAI 3.x minimum version requirement (%s+)", ver.String(), minVersion.String()),
check.WithMessage("OpenShift %s meets RHOAI %s minimum version requirement (%s+)", ver.String(), tv, minVersion.String()),
))
default:
dr.SetCondition(check.NewCondition(
check.ConditionTypeCompatible,
metav1.ConditionFalse,
check.WithReason(check.ReasonVersionIncompatible),
check.WithMessage("OpenShift %s does not meet RHOAI 3.x minimum version requirement (%s+). Upgrade OpenShift to %s or later before upgrading RHOAI",
ver.String(), minVersion.String(), minVersion.String()),
check.WithMessage("OpenShift %s does not meet RHOAI %s minimum version requirement (%s+). Upgrade OpenShift to %s or later before upgrading RHOAI",
ver.String(), tv, minVersion.String(), minVersion.String()),
check.WithImpact(result.ImpactBlocking),
))
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/lint/checks/dependencies/openshift/openshift_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestOpenShiftCheck_VersionMeetsRequirement(t *testing.T) {
"Type": Equal(check.ConditionTypeCompatible),
"Status": Equal(metav1.ConditionTrue),
"Reason": Equal(check.ReasonVersionCompatible),
"Message": ContainSubstring("4.19.9 meets RHOAI 3.x minimum version requirement"),
"Message": ContainSubstring("4.19.9 meets RHOAI 3.0 minimum version requirement"),
}))
}

Expand Down Expand Up @@ -107,7 +107,7 @@ func TestOpenShiftCheck_VersionBelowRequirement(t *testing.T) {
"Status": Equal(metav1.ConditionFalse),
"Reason": Equal(check.ReasonVersionIncompatible),
"Message": And(
ContainSubstring("4.18.5 does not meet RHOAI 3.x minimum version requirement"),
ContainSubstring("4.18.5 does not meet RHOAI 3.0 minimum version requirement"),
ContainSubstring("4.19"),
),
}))
Expand Down Expand Up @@ -138,7 +138,7 @@ func TestOpenShiftCheck_PatchVersionBelowRequirement(t *testing.T) {
"Status": Equal(metav1.ConditionFalse),
"Reason": Equal(check.ReasonVersionIncompatible),
"Message": And(
ContainSubstring("4.19.8 does not meet RHOAI 3.x minimum version requirement"),
ContainSubstring("4.19.8 does not meet RHOAI 3.0 minimum version requirement"),
ContainSubstring("4.19.9"),
),
}))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,27 @@ func (c *Check) CanApply(_ context.Context, target check.Target) (bool, error) {
}

func (c *Check) Validate(ctx context.Context, target check.Target) (*result.DiagnosticResult, error) {
tv := version.MajorMinorLabel(target.TargetVersion)

return validate.Operator(c, target).
WithNames("servicemeshoperator").
WithChannels("stable", "v2.x").
WithConditionBuilder(func(found bool, version string) result.Condition {
WithConditionBuilder(func(found bool, operatorVersion string) result.Condition {
// Inverted logic: NOT finding the operator is good.
if !found {
return check.NewCondition(
check.ConditionTypeCompatible,
metav1.ConditionTrue,
check.WithReason(check.ReasonVersionCompatible),
check.WithMessage("Service Mesh Operator v2 is not installed - ready for RHOAI 3.x upgrade"),
check.WithMessage("Service Mesh Operator v2 is not installed - ready for RHOAI %s upgrade", tv),
)
}

return check.NewCondition(
check.ConditionTypeCompatible,
metav1.ConditionFalse,
check.WithReason(check.ReasonVersionIncompatible),
check.WithMessage("Service Mesh Operator v2 (%s) is installed but no longer required by RHOAI 3.x and should be removed. OpenShift 4.19+ handles service mesh internally", version),
check.WithMessage("Service Mesh Operator v2 (%s) is installed but no longer required by RHOAI %s and should be removed. OpenShift 4.19+ handles service mesh internally", operatorVersion, tv),
)
}).
Run(ctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestServiceMeshOperator2Check_NotInstalled(t *testing.T) {
"Type": Equal(check.ConditionTypeCompatible),
"Status": Equal(metav1.ConditionTrue),
"Reason": Equal(check.ReasonVersionCompatible),
"Message": And(ContainSubstring("not installed"), ContainSubstring("ready for RHOAI 3.x")),
"Message": And(ContainSubstring("not installed"), ContainSubstring("ready for RHOAI 3.0")),
}))
}

Expand Down Expand Up @@ -69,7 +69,7 @@ func TestServiceMeshOperator2Check_InstalledBlocking(t *testing.T) {
"Type": Equal(check.ConditionTypeCompatible),
"Status": Equal(metav1.ConditionFalse),
"Reason": Equal(check.ReasonVersionIncompatible),
"Message": And(ContainSubstring("no longer required by RHOAI 3.x"), ContainSubstring("should be removed")),
"Message": And(ContainSubstring("no longer required by RHOAI 3.0"), ContainSubstring("should be removed")),
}))
g.Expect(result.Annotations).To(HaveKeyWithValue("operator.opendatahub.io/installed-version", "servicemeshoperator.v2.5.0"))
}
Expand Down
Loading