Skip to content

Commit

Permalink
feat: enhances FeatureTracker with spec (opendatahub-io#808)
Browse files Browse the repository at this point in the history
* feat: enhances FeatureTracker with spec (#17)

* initial add tracker spec

* update tests, update crd

* add omitempty to origin struct

* undo accidental tag change

* re add empty line

* move pointer operator

* add testing

* lint

* re-lint changes

* add ownertype, move newOrigin() to shared util

* Update apis/features/v1/features_types.go

Co-authored-by: Bartosz Majsak <bartosz.majsak@gmail.com>

* remove origin from featureinitializer

* modify kserve sm step to match dashboard's

* make dsci servicemesh setup like dashboard's

* fix merge issues, lint

---------

Co-authored-by: Bartosz Majsak <bartosz.majsak@gmail.com>

* restore testing mistakenly removed in merge

* satisfy linter post merge conflicts

* fix linter post merge

* fix post merge issue

* split For() into With + DefinedBy

* rename origin to source, definedby to from

---------

Co-authored-by: Bartosz Majsak <bartosz.majsak@gmail.com>
(cherry picked from commit 591a291)
  • Loading branch information
cam-garrison authored and VaishnaviHire committed Feb 2, 2024
1 parent b790ae1 commit 329e891
Show file tree
Hide file tree
Showing 17 changed files with 367 additions and 205 deletions.
2 changes: 1 addition & 1 deletion apis/datasciencecluster/v1/datasciencecluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
"github.com/opendatahub-io/opendatahub-operator/v2/components/workbenches"
)

// DataScienceCluster defines the desired state of the cluster.
// DataScienceClusterSpec defines the desired state of the cluster.
type DataScienceClusterSpec struct {
// Override and fine tune specific component configurations.
// +operator-sdk:csv:customresourcedefinitions:type=spec,order=1
Expand Down
15 changes: 15 additions & 0 deletions apis/features/v1/features_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ type FeatureTracker struct {
Status FeatureTrackerStatus `json:"status,omitempty"`
}

type OwnerType string

const (
ComponentType OwnerType = "Component"
DSCIType OwnerType = "DSCI"
)

func (s *FeatureTracker) ToOwnerReference() metav1.OwnerReference {
return metav1.OwnerReference{
APIVersion: s.APIVersion,
Expand All @@ -27,8 +34,16 @@ func (s *FeatureTracker) ToOwnerReference() metav1.OwnerReference {
}
}

// Source describes the type of object that created the related Feature to this FeatureTracker.
type Source struct {
Type OwnerType `json:"type,omitempty"`
Name string `json:"name,omitempty"`
}

// FeatureTrackerSpec defines the desired state of FeatureTracker.
type FeatureTrackerSpec struct {
Source Source `json:"source,omitempty"`
AppNamespace string `json:"appNamespace,omitempty"`
}

// FeatureTrackerStatus defines the observed state of FeatureTracker.
Expand Down
16 changes: 16 additions & 0 deletions apis/features/v1/zz_generated.deepcopy.go

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

7 changes: 4 additions & 3 deletions components/kserve/kserve.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,10 @@ func (k *Kserve) configureServerless(instance *dsciv1.DSCInitializationSpec) err
case operatorv1.Managed: // standard workflow to create CR
switch instance.ServiceMesh.ManagementState {
case operatorv1.Unmanaged, operatorv1.Removed:
return fmt.Errorf("ServiceMesh is need to set to 'Managaed' in DSCI CR, it is required by KServe serving field")
return fmt.Errorf("ServiceMesh is need to set to 'Managed' in DSCI CR, it is required by KServe serving field")
}
serverlessInitializer := feature.NewFeaturesInitializer(instance, k.configureServerlessFeatures)

serverlessInitializer := feature.ComponentFeaturesInitializer(k, instance, k.configureServerlessFeatures())

if err := serverlessInitializer.Prepare(); err != nil {
return err
Expand All @@ -199,7 +200,7 @@ func (k *Kserve) configureServerless(instance *dsciv1.DSCInitializationSpec) err
}

func (k *Kserve) removeServerlessFeatures(instance *dsciv1.DSCInitializationSpec) error {
serverlessInitializer := feature.NewFeaturesInitializer(instance, k.configureServerlessFeatures)
serverlessInitializer := feature.ComponentFeaturesInitializer(k, instance, k.configureServerlessFeatures())

if err := serverlessInitializer.Prepare(); err != nil {
return err
Expand Down
86 changes: 44 additions & 42 deletions components/kserve/serverless_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,52 @@ const (
templatesDir = "templates/serverless"
)

func (k *Kserve) configureServerlessFeatures(s *feature.FeaturesInitializer) error {
servingDeployment, err := feature.CreateFeature("serverless-serving-deployment").
For(s.DSCInitializationSpec).
Manifests(
path.Join(templatesDir, "serving-install"),
).
WithData(PopulateComponentSettings(k)).
PreConditions(
serverless.EnsureServerlessOperatorInstalled,
serverless.EnsureServerlessAbsent,
servicemesh.EnsureServiceMeshInstalled,
feature.CreateNamespaceIfNotExists(knativeServingNamespace),
).
PostConditions(
feature.WaitForPodsToBeReady(knativeServingNamespace),
).
Load()
if err != nil {
return err
}
s.Features = append(s.Features, servingDeployment)
func (k *Kserve) configureServerlessFeatures() feature.DefinedFeatures {
return func(initializer *feature.FeaturesInitializer) error {
servingDeployment, err := feature.CreateFeature("serverless-serving-deployment").
With(initializer.DSCInitializationSpec).
From(initializer.Source).
Manifests(
path.Join(templatesDir, "serving-install"),
).
WithData(PopulateComponentSettings(k)).
PreConditions(
serverless.EnsureServerlessOperatorInstalled,
serverless.EnsureServerlessAbsent,
servicemesh.EnsureServiceMeshInstalled,
feature.CreateNamespaceIfNotExists(knativeServingNamespace),
).
PostConditions(
feature.WaitForPodsToBeReady(knativeServingNamespace),
).
Load()
if err != nil {
return err
}
initializer.Features = append(initializer.Features, servingDeployment)

servingIstioGateways, err := feature.CreateFeature("serverless-serving-gateways").
For(s.DSCInitializationSpec).
PreConditions(
// Check serverless is installed
feature.WaitForResourceToBeCreated(knativeServingNamespace, gvr.KnativeServing),
).
WithData(
serverless.ServingDefaultValues,
serverless.ServingIngressDomain,
PopulateComponentSettings(k),
).
WithResources(serverless.ServingCertificateResource).
Manifests(
path.Join(templatesDir, "serving-istio-gateways"),
).
Load()
if err != nil {
return err
servingIstioGateways, err := feature.CreateFeature("serverless-serving-gateways").
With(initializer.DSCInitializationSpec).
From(initializer.Source).
PreConditions(
// Check serverless is installed
feature.WaitForResourceToBeCreated(knativeServingNamespace, gvr.KnativeServing)).
WithData(
serverless.ServingDefaultValues,
serverless.ServingIngressDomain,
PopulateComponentSettings(k),
).
WithResources(serverless.ServingCertificateResource).
Manifests(
path.Join(templatesDir, "serving-istio-gateways"),
).
Load()
if err != nil {
return err
}
initializer.Features = append(initializer.Features, servingIstioGateways)
return nil
}
s.Features = append(s.Features, servingIstioGateways)

return nil
}

func PopulateComponentSettings(k *Kserve) feature.Action {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ spec:
metadata:
type: object
spec:
description: DataScienceCluster defines the desired state of the cluster.
description: DataScienceClusterSpec defines the desired state of the cluster.
properties:
components:
description: Override and fine tune specific component configurations.
Expand Down
12 changes: 12 additions & 0 deletions config/crd/bases/features.opendatahub.io_featuretrackers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ spec:
type: object
spec:
description: FeatureTrackerSpec defines the desired state of FeatureTracker.
properties:
appNamespace:
type: string
source:
description: Source describes the type of object that created the
related Feature to this FeatureTracker.
properties:
name:
type: string
type:
type: string
type: object
type: object
status:
description: FeatureTrackerStatus defines the observed state of FeatureTracker.
Expand Down
71 changes: 37 additions & 34 deletions controllers/dscinitialization/servicemesh_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const templatesDir = "templates/servicemesh"
func (r *DSCInitializationReconciler) configureServiceMesh(instance *dsciv1.DSCInitialization) error {
switch instance.Spec.ServiceMesh.ManagementState {
case operatorv1.Managed:
serviceMeshInitializer := feature.NewFeaturesInitializer(&instance.Spec, configureServiceMeshFeatures)
serviceMeshInitializer := feature.ClusterFeaturesInitializer(instance, configureServiceMeshFeatures())
if err := serviceMeshInitializer.Prepare(); err != nil {
r.Log.Error(err, "failed configuring service mesh resources")
r.Recorder.Eventf(instance, corev1.EventTypeWarning, "DSCInitializationReconcileError", "failed configuring service mesh resources")
Expand All @@ -43,8 +43,7 @@ func (r *DSCInitializationReconciler) configureServiceMesh(instance *dsciv1.DSCI
func (r *DSCInitializationReconciler) removeServiceMesh(instance *dsciv1.DSCInitialization) error {
// on condition of Managed, do not handle Removed when set to Removed it trigger DSCI reconcile to cleanup
if instance.Spec.ServiceMesh.ManagementState == operatorv1.Managed {
serviceMeshInitializer := feature.NewFeaturesInitializer(&instance.Spec, configureServiceMeshFeatures)

serviceMeshInitializer := feature.ClusterFeaturesInitializer(instance, configureServiceMeshFeatures())
if err := serviceMeshInitializer.Prepare(); err != nil {
r.Log.Error(err, "failed configuring service mesh resources")
r.Recorder.Eventf(instance, corev1.EventTypeWarning, "DSCInitializationReconcileError", "failed configuring service mesh resources")
Expand All @@ -63,42 +62,46 @@ func (r *DSCInitializationReconciler) removeServiceMesh(instance *dsciv1.DSCInit
return nil
}

func configureServiceMeshFeatures(s *feature.FeaturesInitializer) error {
serviceMeshSpec := s.ServiceMesh

smcpCreation, errSmcp := feature.CreateFeature("mesh-control-plane-creation").
For(s.DSCInitializationSpec).
Manifests(
path.Join(templatesDir, "base", "create-smcp.tmpl"),
).
PreConditions(
servicemesh.EnsureServiceMeshOperatorInstalled,
feature.CreateNamespaceIfNotExists(serviceMeshSpec.ControlPlane.Namespace),
).
PostConditions(
feature.WaitForPodsToBeReady(serviceMeshSpec.ControlPlane.Namespace),
).
Load()
if errSmcp != nil {
return errSmcp
}
s.Features = append(s.Features, smcpCreation)
func configureServiceMeshFeatures() feature.DefinedFeatures {
return func(initializer *feature.FeaturesInitializer) error {
serviceMeshSpec := initializer.DSCInitializationSpec.ServiceMesh

if serviceMeshSpec.ControlPlane.MetricsCollection == "Istio" {
metricsCollection, errMetrics := feature.CreateFeature("mesh-metrics-collection").
For(s.DSCInitializationSpec).
smcpCreation, errSmcp := feature.CreateFeature("mesh-control-plane-creation").
With(initializer.DSCInitializationSpec).
From(initializer.Source).
Manifests(
path.Join(templatesDir, "base", "create-smcp.tmpl"),
).
PreConditions(
servicemesh.EnsureServiceMeshInstalled,
servicemesh.EnsureServiceMeshOperatorInstalled,
feature.CreateNamespaceIfNotExists(serviceMeshSpec.ControlPlane.Namespace),
).
Manifests(
path.Join(templatesDir, "metrics-collection"),
PostConditions(
feature.WaitForPodsToBeReady(serviceMeshSpec.ControlPlane.Namespace),
).
Load()
if errMetrics != nil {
return errMetrics

if errSmcp != nil {
return errSmcp
}
initializer.Features = append(initializer.Features, smcpCreation)

if serviceMeshSpec.ControlPlane.MetricsCollection == "Istio" {
metricsCollection, errMetrics := feature.CreateFeature("mesh-metrics-collection").
With(initializer.DSCInitializationSpec).
From(initializer.Source).
Manifests(
path.Join(templatesDir, "metrics-collection"),
).
PreConditions(
servicemesh.EnsureServiceMeshInstalled,
).
Load()
if errMetrics != nil {
return errMetrics
}
initializer.Features = append(initializer.Features, metricsCollection)
}
s.Features = append(s.Features, metricsCollection)
return nil
}

return nil
}
32 changes: 26 additions & 6 deletions pkg/feature/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,44 @@ type featureBuilder struct {
fsys fs.FS
}

func CreateFeature(name string) *featureBuilder { //nolint:golint,revive //No need to export featureBuilder.
return &featureBuilder{
func CreateFeature(name string) *featureName { //nolint:golint,revive //No need to export featureBuilder.
return &featureName{
name: name,
fsys: embeddedFiles,
}
}

func (fb *featureBuilder) For(spec *v1.DSCInitializationSpec) *featureBuilder {
type featureName struct {
name string
}

func (fn *featureName) With(spec *v1.DSCInitializationSpec) *featureSource {
return &featureSource{
spec: spec,
name: fn.name,
}
}

type featureSource struct {
spec *v1.DSCInitializationSpec
name string
}

func (fo *featureSource) From(source featurev1.Source) *featureBuilder {
createSpec := func(f *Feature) error {
f.Spec = &Spec{
ServiceMeshSpec: &spec.ServiceMesh,
ServiceMeshSpec: &fo.spec.ServiceMesh,
Serving: &infrav1.ServingSpec{},
AppNamespace: spec.ApplicationsNamespace,
Source: &source,
AppNamespace: fo.spec.ApplicationsNamespace,
}

return nil
}

fb := &featureBuilder{
name: fo.name,
fsys: embeddedFiles,
}
// Ensures creation of .Spec object is always invoked first
fb.builders = append([]partialBuilder{createSpec}, fb.builders...)

Expand Down
4 changes: 4 additions & 0 deletions pkg/feature/feature_tracker_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ func (f *Feature) getFeatureTracker() (*featurev1.FeatureTracker, error) {
ObjectMeta: metav1.ObjectMeta{
Name: f.Spec.AppNamespace + "-" + common.TrimToRFC1123Name(f.Name),
},
Spec: featurev1.FeatureTrackerSpec{
Source: *f.Spec.Source,
AppNamespace: f.Spec.AppNamespace,
},
}

err := f.Client.Get(context.Background(), client.ObjectKeyFromObject(tracker), tracker)
Expand Down
Loading

0 comments on commit 329e891

Please sign in to comment.