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
3 changes: 3 additions & 0 deletions api/v1alpha1/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,7 @@ const (

// ReasonServiceNotFound indicates the referenced service entity could not be found in OpenMetadata.
ReasonServiceNotFound = "ServiceNotFound"

// ReasonOwnerResolutionFailed indicates an owner could not be resolved to an OpenMetadata UUID.
ReasonOwnerResolutionFailed = "OwnerResolutionFailed"
)
12 changes: 10 additions & 2 deletions api/v1alpha1/ingestionpipeline_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ const (
)

// EntityType represents the type of an entity referenced in OpenMetadata.
// +kubebuilder:validation:Enum=databaseService;messagingService;dashboardService;pipelineService;mlmodelService;storageService;searchService;metadataService;apiService;testSuite
// +kubebuilder:validation:Enum=databaseService;messagingService;dashboardService;pipelineService;mlmodelService;storageService;searchService;metadataService;apiService;testSuite;user;team
type EntityType string

Comment thread
berimbolo13 marked this conversation as resolved.
// Entity type constants for service entity references.
// Entity type constants for entity references.
const (
EntityTypeDatabaseService EntityType = "databaseService"
EntityTypeMessagingService EntityType = "messagingService"
Expand All @@ -55,6 +55,8 @@ const (
EntityTypeMetadataService EntityType = "metadataService"
EntityTypeAPIService EntityType = "apiService"
EntityTypeTestSuite EntityType = "testSuite"
EntityTypeUser EntityType = "user"
EntityTypeTeam EntityType = "team"
)

// IngestionPipelineSpec defines the desired state of an IngestionPipeline.
Expand Down Expand Up @@ -95,6 +97,12 @@ type IngestionPipelineOMSpec struct {
// Description of the pipeline.
// +optional
Description string `json:"description,omitempty"`

// Owners is the list of users and/or teams that own this pipeline in OpenMetadata.
// Each entry references a user or team by fullyQualifiedName; the operator
// resolves these to UUIDs at reconcile time.
// +optional
Owners []EntityReference `json:"owners,omitempty"`
}

// EntityReference identifies an entity in OpenMetadata by FQN and type.
Expand Down
6 changes: 6 additions & 0 deletions api/v1alpha1/openmetadataservice_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ type ServiceOMSpec struct {
// +optional
Description string `json:"description,omitempty"`

// Owners is the list of users and/or teams that own this service in OpenMetadata.
// Each entry references a user or team by fullyQualifiedName; the operator
// resolves these to UUIDs at reconcile time.
// +optional
Owners []EntityReference `json:"owners,omitempty"`

// Connection holds the opaque connection configuration forwarded to OpenMetadata.
Connection ConnectionSpec `json:"connection"`
}
Expand Down
6 changes: 6 additions & 0 deletions api/v1alpha1/openmetadatatestcase_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ type TestCaseOMSpec struct {
// ComputePassedFailedRowCount controls whether to compute row counts.
// +optional
ComputePassedFailedRowCount bool `json:"computePassedFailedRowCount,omitempty"`

// Owners is the list of users and/or teams that own this test case in OpenMetadata.
// Each entry references a user or team by fullyQualifiedName; the operator
// resolves these to UUIDs at reconcile time.
// +optional
Owners []EntityReference `json:"owners,omitempty"`
}

// TestCaseParameterValue is a name-value pair for test parameters.
Expand Down
15 changes: 15 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

37 changes: 37 additions & 0 deletions config/crd/bases/openmetadata.vortexa.com_ingestionpipelines.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,41 @@ spec:
description: DisplayName is a human-readable name shown in the
OpenMetadata UI.
type: string
owners:
description: |-
Owners is the list of users and/or teams that own this pipeline in OpenMetadata.
Each entry references a user or team by fullyQualifiedName; the operator
resolves these to UUIDs at reconcile time.
items:
description: EntityReference identifies an entity in OpenMetadata
by FQN and type.
properties:
fullyQualifiedName:
description: FullyQualifiedName of the referenced entity.
minLength: 1
type: string
type:
description: Type of the referenced entity (e.g. "databaseService",
"testSuite").
enum:
- databaseService
- messagingService
- dashboardService
- pipelineService
- mlmodelService
Comment thread
berimbolo13 marked this conversation as resolved.
- storageService
- searchService
- metadataService
- apiService
- testSuite
- user
- team
type: string
required:
- fullyQualifiedName
- type
type: object
type: array
pipelineType:
description: PipelineType is the OpenMetadata pipeline type.
enum:
Expand Down Expand Up @@ -147,6 +182,8 @@ spec:
- metadataService
- apiService
- testSuite
- user
- team
Comment thread
berimbolo13 marked this conversation as resolved.
type: string
required:
- fullyQualifiedName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,41 @@ spec:
description: DisplayName is a human-readable name shown in the
OpenMetadata UI.
type: string
owners:
description: |-
Owners is the list of users and/or teams that own this service in OpenMetadata.
Each entry references a user or team by fullyQualifiedName; the operator
resolves these to UUIDs at reconcile time.
items:
description: EntityReference identifies an entity in OpenMetadata
by FQN and type.
properties:
fullyQualifiedName:
description: FullyQualifiedName of the referenced entity.
minLength: 1
type: string
type:
description: Type of the referenced entity (e.g. "databaseService",
"testSuite").
enum:
- databaseService
- messagingService
- dashboardService
- pipelineService
- mlmodelService
- storageService
- searchService
- metadataService
- apiService
- testSuite
Comment thread
berimbolo13 marked this conversation as resolved.
- user
- team
type: string
required:
- fullyQualifiedName
- type
type: object
type: array
serviceType:
description: |-
ServiceType is the OpenMetadata service type (e.g. Postgres, Athena, Kafka).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,41 @@ spec:
<#E::table::service.database.schema.table::columns::colName> for column tests
minLength: 1
type: string
owners:
description: |-
Owners is the list of users and/or teams that own this test case in OpenMetadata.
Each entry references a user or team by fullyQualifiedName; the operator
resolves these to UUIDs at reconcile time.
items:
description: EntityReference identifies an entity in OpenMetadata
by FQN and type.
properties:
fullyQualifiedName:
description: FullyQualifiedName of the referenced entity.
minLength: 1
type: string
type:
description: Type of the referenced entity (e.g. "databaseService",
"testSuite").
enum:
- databaseService
- messagingService
- dashboardService
- pipelineService
- mlmodelService
- storageService
- searchService
- metadataService
- apiService
- testSuite
Comment thread
berimbolo13 marked this conversation as resolved.
- user
- team
type: string
required:
- fullyQualifiedName
- type
type: object
type: array
parameterValues:
description: ParameterValues are the test-specific parameters.
items:
Expand Down
4 changes: 4 additions & 0 deletions internal/controller/openmetadataservice_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ func (s *stubClient) DeleteService(_ context.Context, _, _ string) error {
return s.deleteErr
}

func (s *stubClient) GetEntityByName(_ context.Context, _, _ string) (string, error) {
return "", &omclient.APIError{StatusCode: 404, Body: "not found"}
}

func newTestReconciler(stub omclient.ServiceClient) *OpenMetadataServiceReconciler {
return &OpenMetadataServiceReconciler{
Client: k8sClient,
Expand Down
4 changes: 4 additions & 0 deletions internal/controller/openmetadatatestcase_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ func (s *stubTestCaseClient) DeleteTestCase(_ context.Context, _ string) error {
return s.deleteErr
}

func (s *stubTestCaseClient) GetEntityByName(_ context.Context, _, _ string) (string, error) {
return "", &omclient.APIError{StatusCode: 404, Body: "not found"}
}

func newTestTestCaseReconciler(stub omclient.TestCaseClient) *OpenMetadataTestCaseReconciler {
return &OpenMetadataTestCaseReconciler{
Client: k8sClient,
Expand Down
66 changes: 66 additions & 0 deletions internal/handler/owner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright 2026.

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 handler

import (
"context"
"errors"
"fmt"

omv1alpha1 "github.com/VorTECHsa/openmetadata-operator/api/v1alpha1"
"github.com/VorTECHsa/openmetadata-operator/internal/omclient"
)

// ErrUnsupportedOwnerType signals a permanent spec error: an owner has a type
// other than "user" or "team". Distinguished from transient errors so callers
// can skip requeue.
var ErrUnsupportedOwnerType = errors.New("unsupported owner type")

// ownerResolver is the narrow interface needed by resolveOwners
type ownerResolver interface {
GetEntityByName(ctx context.Context, entityTypePath, fqn string) (string, error)
}

// ownerTypePath maps owner EntityType values to OpenMetadata API path segments.
var ownerTypePath = map[omv1alpha1.EntityType]string{
omv1alpha1.EntityTypeUser: "users",
omv1alpha1.EntityTypeTeam: "teams",
}

// resolveOwners converts user-facing owner references (FQN + type) to API
// owner references (id + type) by resolving each FQN via GetEntityByName.
// Returns nil for an empty input so callers omit the owners field entirely.
// Wraps the underlying error so callers can use errors.Is(err, ErrUnsupportedOwnerType)
// to distinguish permanent spec errors from transient ones.
func resolveOwners(ctx context.Context, client ownerResolver, owners []omv1alpha1.EntityReference) ([]omclient.EntityRef, error) {
if len(owners) == 0 {
return nil, nil
}
resolved := make([]omclient.EntityRef, 0, len(owners))
for _, o := range owners {
path, ok := ownerTypePath[o.Type]
if !ok {
return nil, fmt.Errorf("%w %q (must be user or team)", ErrUnsupportedOwnerType, o.Type)
}
id, err := client.GetEntityByName(ctx, path, o.FullyQualifiedName)
if err != nil {
return nil, fmt.Errorf("resolving owner %s %q: %w", o.Type, o.FullyQualifiedName, err)
}
resolved = append(resolved, omclient.EntityRef{ID: id, Type: string(o.Type)})
}
return resolved, nil
}
Loading
Loading