Repository: odahu/odahu-flow (language: python)

Issue:
redundant createdAt and updatedAt keys for training, packaging, deployment
**Describe the bug**
there are two `createdAt` and `updatedAt` keys

![MicrosoftTeams-image (5)](https://user-images.githubusercontent.com/49116723/94797749-3c5e2c00-03e9-11eb-8688-6af6c1a7df18.png)

**expected behaviour**
should be only one `createdAt` and `updatedAt` (not in the `status`)

**expected behaviour**
there are two `createdAt` and `updatedAt` keys



Discussion / hints:
These fields are indeed redundant. They are gonna be deprecated since 1.3 and planned to be completely removed in 1.4.
As I see, this issue is not resolved, for now, so I think we should just put tag: `1.4` and place to `To Do` column so that we won't forget to do that or create another task (feature) for that and put the link to this task there.
@BPylypenko, @keshamin 
Currently, these fields are deprecated
What do you think about deferring their removal to ODAHU 2.0 release? Motivation is not to break backward compatibility for clients in a minor release


Fix patch:
diff --git a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_connections.yaml b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_connections.yaml
index cb82fdafe..b38185058 100644
--- a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_connections.yaml
+++ b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_connections.yaml
@@ -78,18 +78,12 @@ spec:
         status:
           description: ConnectionStatus defines the observed state of ConnectionName.
           properties:
-            createdAt:
-              format: date-time
-              type: string
             secretName:
               description: Kubernetes secret name
               type: string
             serviceAccount:
               description: Kubernetes service account
               type: string
-            updatedAt:
-              format: date-time
-              type: string
           type: object
       type: object
   version: v1alpha1
diff --git a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modeldeployments.yaml b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modeldeployments.yaml
index ddb94b753..8a14f8c1f 100644
--- a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modeldeployments.yaml
+++ b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modeldeployments.yaml
@@ -135,9 +135,6 @@ spec:
               description: Number of available pods
               format: int32
               type: integer
-            createdAt:
-              format: date-time
-              type: string
             deployment:
               description: The model k8s deployment name
               type: string
@@ -165,9 +162,6 @@ spec:
                 resource are wrong. For example, there is not a model                  image
                 in a Docker registry.   "Ready" - A model was deployed successfully.
               type: string
-            updatedAt:
-              format: date-time
-              type: string
           required:
           - availableReplicas
           - replicas
diff --git a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modelpackagings.yaml b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modelpackagings.yaml
index 3c759c2fe..efb3ca86f 100644
--- a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modelpackagings.yaml
+++ b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modelpackagings.yaml
@@ -98,9 +98,6 @@ spec:
         status:
           description: ModelPackagingStatus defines the observed state of ModelPackaging
           properties:
-            createdAt:
-              format: date-time
-              type: string
             exitCode:
               description: Pod exit code
               format: int32
@@ -133,9 +130,6 @@ spec:
             state:
               description: Model Packaging State
               type: string
-            updatedAt:
-              format: date-time
-              type: string
           type: object
       type: object
   version: v1alpha1
diff --git a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modelroutes.yaml b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modelroutes.yaml
index f865bad27..a5d9f5fac 100644
--- a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modelroutes.yaml
+++ b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modelroutes.yaml
@@ -78,18 +78,12 @@ spec:
         status:
           description: ModelRouteStatus defines the observed state of ModelRoute
           properties:
-            createdAt:
-              format: date-time
-              type: string
             edgeUrl:
               description: Full url with prefix to a model deployment service
               type: string
             state:
               description: State of Model Route
               type: string
-            updatedAt:
-              format: date-time
-              type: string
           required:
           - edgeUrl
           - state
diff --git a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modeltrainings.yaml b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modeltrainings.yaml
index 8c45f1278..d6cd1261e 100644
--- a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modeltrainings.yaml
+++ b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_modeltrainings.yaml
@@ -221,9 +221,6 @@ spec:
                 - runId
                 type: object
               type: array
-            createdAt:
-              format: date-time
-              type: string
             exitCode:
               description: Pod exit code
               format: int32
@@ -240,9 +237,6 @@ spec:
             state:
               description: Model Packaging State
               type: string
-            updatedAt:
-              format: date-time
-              type: string
           type: object
       type: object
   version: v1alpha1
diff --git a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_packagingintegrations.yaml b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_packagingintegrations.yaml
index 249ffab62..122199c02 100644
--- a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_packagingintegrations.yaml
+++ b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_packagingintegrations.yaml
@@ -83,13 +83,6 @@ spec:
           type: object
         status:
           description: PackagingIntegrationStatus defines the observed state of PackagingIntegration
-          properties:
-            createdAt:
-              format: date-time
-              type: string
-            updatedAt:
-              format: date-time
-              type: string
           type: object
       type: object
   version: v1alpha1
diff --git a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_toolchainintegrations.yaml b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_toolchainintegrations.yaml
index 3d0f02afd..662b26829 100644
--- a/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_toolchainintegrations.yaml
+++ b/helms/odahu-flow-core/templates/operator/generated/odahuflow.odahu.org_toolchainintegrations.yaml
@@ -52,13 +52,6 @@ spec:
           type: object
         status:
           description: ToolchainIntegrationStatus defines the observed state of ToolchainIntegration
-          properties:
-            createdAt:
-              format: date-time
-              type: string
-            updatedAt:
-              format: date-time
-              type: string
           type: object
       type: object
   version: v1alpha1
diff --git a/packages/operator/api/v1alpha1/connection_types.go b/packages/operator/api/v1alpha1/connection_types.go
index cda309e7b..596101c49 100644
--- a/packages/operator/api/v1alpha1/connection_types.go
+++ b/packages/operator/api/v1alpha1/connection_types.go
@@ -61,10 +61,6 @@ type ConnectionStatus struct {
 	SecretName *string `json:"secretName,omitempty"`
 	// Kubernetes service account
 	ServiceAccountName *string `json:"serviceAccount,omitempty"`
-	// Info about create and update
-	//CreatedAt *metav1.Time `json:"createdAt,omitempty"`
-	//UpdatedAt *metav1.Time `json:"updatedAt,omitempty"`
-	Modifiable `json:",inline"`
 }
 
 // +kubebuilder:object:root=true
diff --git a/packages/operator/api/v1alpha1/modeldeployment_types.go b/packages/operator/api/v1alpha1/modeldeployment_types.go
index 6ed1e4dac..ea8451f69 100644
--- a/packages/operator/api/v1alpha1/modeldeployment_types.go
+++ b/packages/operator/api/v1alpha1/modeldeployment_types.go
@@ -82,10 +82,6 @@ type ModelDeploymentStatus struct {
 	ModelName string `json:"modelName,omitempty"`
 	// Model version discovered in ModelDeployment
 	ModelVersion string `json:"modelVersion,omitempty"`
-	// DEPRECATED Info about create and update
-	//CreatedAt *metav1.Time `json:"createdAt,omitempty"`
-	//UpdatedAt *metav1.Time `json:"updatedAt,omitempty"`
-	Modifiable `json:",inline"`
 }
 
 func (in ModelDeploymentSpec) Value() (driver.Value, error) {
diff --git a/packages/operator/api/v1alpha1/modelpackaging_types.go b/packages/operator/api/v1alpha1/modelpackaging_types.go
index 96cb8e9e0..b73bd03c8 100644
--- a/packages/operator/api/v1alpha1/modelpackaging_types.go
+++ b/packages/operator/api/v1alpha1/modelpackaging_types.go
@@ -62,10 +62,6 @@ type ModelPackagingStatus struct {
 	Message *string `json:"message,omitempty"`
 	// List of packaing results
 	Results []ModelPackagingResult `json:"results,omitempty"`
-	// DEPRECATED Info about create and update
-	//CreatedAt *metav1.Time `json:"createdAt,omitempty"`
-	//UpdatedAt *metav1.Time `json:"updatedAt,omitempty"`
-	Modifiable `json:",inline"`
 }
 
 // ModelPackagingState defines current state
diff --git a/packages/operator/api/v1alpha1/modelroute_types.go b/packages/operator/api/v1alpha1/modelroute_types.go
index 01a324cec..6055bc6a1 100644
--- a/packages/operator/api/v1alpha1/modelroute_types.go
+++ b/packages/operator/api/v1alpha1/modelroute_types.go
@@ -55,10 +55,6 @@ type ModelRouteStatus struct {
 	EdgeURL string `json:"edgeUrl"`
 	// State of Model Route
 	State ModelRouteState `json:"state"`
-	// Info about create and update
-	//CreatedAt *metav1.Time `json:"createdAt,omitempty"`
-	//UpdatedAt *metav1.Time `json:"updatedAt,omitempty"`
-	Modifiable `json:",inline"`
 }
 
 func (in ModelRouteSpec) Value() (driver.Value, error) {
@@ -87,7 +83,6 @@ func (in *ModelRouteStatus) Scan(value interface{}) error {
 	return res
 }
 
-
 // +kubebuilder:object:root=true
 
 // ModelRoute is the Schema for the modelroutes API
diff --git a/packages/operator/api/v1alpha1/modeltraining_types.go b/packages/operator/api/v1alpha1/modeltraining_types.go
index a43abbd49..5653266cb 100644
--- a/packages/operator/api/v1alpha1/modeltraining_types.go
+++ b/packages/operator/api/v1alpha1/modeltraining_types.go
@@ -133,10 +133,6 @@ type ModelTrainingStatus struct {
 	Message *string `json:"message,omitempty"`
 	// List of training results
 	Artifacts []TrainingResult `json:"artifacts,omitempty"`
-	// DEPRECATED Info about create and update
-	//CreatedAt *metav1.Time `json:"createdAt,omitempty"`
-	//UpdatedAt *metav1.Time `json:"updatedAt,omitempty"`
-	Modifiable `json:",inline"`
 }
 
 func (spec ModelTrainingSpec) Value() (driver.Value, error) {
diff --git a/packages/operator/api/v1alpha1/packagingintegration_types.go b/packages/operator/api/v1alpha1/packagingintegration_types.go
index e30c840c7..bdaf18041 100644
--- a/packages/operator/api/v1alpha1/packagingintegration_types.go
+++ b/packages/operator/api/v1alpha1/packagingintegration_types.go
@@ -49,12 +49,7 @@ type PackagingIntegrationSpec struct {
 }
 
 // PackagingIntegrationStatus defines the observed state of PackagingIntegration
-type PackagingIntegrationStatus struct {
-	// Info about create and update
-	//CreatedAt *metav1.Time `json:"createdAt,omitempty"`
-	//UpdatedAt *metav1.Time `json:"updatedAt,omitempty"`
-	Modifiable `json:",inline"`
-}
+type PackagingIntegrationStatus struct{}
 
 func (piStatus PackagingIntegrationStatus) Value() (driver.Value, error) {
 	return json.Marshal(piStatus)
diff --git a/packages/operator/api/v1alpha1/toolchainintegration_types.go b/packages/operator/api/v1alpha1/toolchainintegration_types.go
index 89712832d..47c8afb95 100644
--- a/packages/operator/api/v1alpha1/toolchainintegration_types.go
+++ b/packages/operator/api/v1alpha1/toolchainintegration_types.go
@@ -34,12 +34,7 @@ type ToolchainIntegrationSpec struct {
 }
 
 // ToolchainIntegrationStatus defines the observed state of ToolchainIntegration
-type ToolchainIntegrationStatus struct {
-	// Info about create and update
-	//CreatedAt *metav1.Time `json:"createdAt,omitempty"`
-	//UpdatedAt *metav1.Time `json:"updatedAt,omitempty"`
-	Modifiable `json:",inline"`
-}
+type ToolchainIntegrationStatus struct{}
 
 func (tiSpec ToolchainIntegrationSpec) Value() (driver.Value, error) {
 	return json.Marshal(tiSpec)
diff --git a/packages/operator/api/v1alpha1/zz_generated.deepcopy.go b/packages/operator/api/v1alpha1/zz_generated.deepcopy.go
index 34dfc1de4..c0b6dfca5 100644
--- a/packages/operator/api/v1alpha1/zz_generated.deepcopy.go
+++ b/packages/operator/api/v1alpha1/zz_generated.deepcopy.go
@@ -240,7 +240,6 @@ func (in *ConnectionStatus) DeepCopyInto(out *ConnectionStatus) {
 		*out = new(string)
 		**out = **in
 	}
-	in.Modifiable.DeepCopyInto(&out.Modifiable)
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectionStatus.
@@ -449,7 +448,6 @@ func (in *ModelDeploymentStatus) DeepCopyInto(out *ModelDeploymentStatus) {
 		in, out := &in.LastCredsUpdatedTime, &out.LastCredsUpdatedTime
 		*out = (*in).DeepCopy()
 	}
-	in.Modifiable.DeepCopyInto(&out.Modifiable)
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModelDeploymentStatus.
@@ -646,7 +644,6 @@ func (in *ModelPackagingStatus) DeepCopyInto(out *ModelPackagingStatus) {
 		*out = make([]ModelPackagingResult, len(*in))
 		copy(*out, *in)
 	}
-	in.Modifiable.DeepCopyInto(&out.Modifiable)
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModelPackagingStatus.
@@ -665,7 +662,7 @@ func (in *ModelRoute) DeepCopyInto(out *ModelRoute) {
 	out.TypeMeta = in.TypeMeta
 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
 	in.Spec.DeepCopyInto(&out.Spec)
-	in.Status.DeepCopyInto(&out.Status)
+	out.Status = in.Status
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModelRoute.
@@ -748,7 +745,6 @@ func (in *ModelRouteSpec) DeepCopy() *ModelRouteSpec {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ModelRouteStatus) DeepCopyInto(out *ModelRouteStatus) {
 	*out = *in
-	in.Modifiable.DeepCopyInto(&out.Modifiable)
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModelRouteStatus.
@@ -919,7 +915,6 @@ func (in *ModelTrainingStatus) DeepCopyInto(out *ModelTrainingStatus) {
 		*out = make([]TrainingResult, len(*in))
 		copy(*out, *in)
 	}
-	in.Modifiable.DeepCopyInto(&out.Modifiable)
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModelTrainingStatus.
@@ -976,7 +971,7 @@ func (in *PackagingIntegration) DeepCopyInto(out *PackagingIntegration) {
 	out.TypeMeta = in.TypeMeta
 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
 	in.Spec.DeepCopyInto(&out.Spec)
-	in.Status.DeepCopyInto(&out.Status)
+	out.Status = in.Status
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackagingIntegration.
@@ -1048,7 +1043,6 @@ func (in *PackagingIntegrationSpec) DeepCopy() *PackagingIntegrationSpec {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *PackagingIntegrationStatus) DeepCopyInto(out *PackagingIntegrationStatus) {
 	*out = *in
-	in.Modifiable.DeepCopyInto(&out.Modifiable)
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackagingIntegrationStatus.
@@ -1195,7 +1189,7 @@ func (in *ToolchainIntegration) DeepCopyInto(out *ToolchainIntegration) {
 	out.TypeMeta = in.TypeMeta
 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
 	in.Spec.DeepCopyInto(&out.Spec)
-	in.Status.DeepCopyInto(&out.Status)
+	out.Status = in.Status
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToolchainIntegration.
@@ -1273,7 +1267,6 @@ func (in *ToolchainIntegrationSpec) DeepCopy() *ToolchainIntegrationSpec {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ToolchainIntegrationStatus) DeepCopyInto(out *ToolchainIntegrationStatus) {
 	*out = *in
-	in.Modifiable.DeepCopyInto(&out.Modifiable)
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ToolchainIntegrationStatus.
diff --git a/packages/operator/docs/docs.go b/packages/operator/docs/docs.go
index c1d0b08f7..6e5bed432 100644
--- a/packages/operator/docs/docs.go
+++ b/packages/operator/docs/docs.go
@@ -3220,6 +3220,10 @@ var doc = `{
         "Connection": {
             "type": "object",
             "properties": {
+                "createdAt": {
+                    "description": "CreatedAt",
+                    "type": "string"
+                },
                 "id": {
                     "description": "Connection id",
                     "type": "string"
@@ -3233,6 +3237,10 @@ var doc = `{
                     "description": "Connection status",
                     "type": "object",
                     "$ref": "#/definitions/ConnectionStatus"
+                },
+                "updatedAt": {
+                    "description": "UpdatedAt",
+                    "type": "string"
                 }
             }
         },
@@ -3520,6 +3528,10 @@ var doc = `{
         "PackagingIntegration": {
             "type": "object",
             "properties": {
+                "createdAt": {
+                    "description": "CreatedAt",
+                    "type": "string"
+                },
                 "id": {
                     "description": "Packaging integration id",
                     "type": "string"
@@ -3533,6 +3545,10 @@ var doc = `{
                     "description": "Packaging integration status",
                     "type": "object",
                     "$ref": "#/definitions/PackagingIntegrationStatus"
+                },
+                "updatedAt": {
+                    "description": "UpdatedAt",
+                    "type": "string"
                 }
             }
         },
@@ -3684,6 +3700,10 @@ var doc = `{
         "ToolchainIntegration": {
             "type": "object",
             "properties": {
+                "createdAt": {
+                    "description": "CreatedAt",
+                    "type": "string"
+                },
                 "id": {
                     "description": "Toolchain integration id",
                     "type": "string"
@@ -3697,6 +3717,10 @@ var doc = `{
                     "description": "Toolchain integration status",
                     "type": "object",
                     "$ref": "#/definitions/ToolchainIntegrationStatus"
+                },
+                "updatedAt": {
+                    "description": "UpdatedAt",
+                    "type": "string"
                 }
             }
         },
@@ -3780,9 +3804,6 @@ var doc = `{
         "ConnectionStatus": {
             "type": "object",
             "properties": {
-                "createdAt": {
-                    "type": "string"
-                },
                 "secretName": {
                     "description": "Kubernetes secret name",
                     "type": "string"
@@ -3790,9 +3811,6 @@ var doc = `{
                 "serviceAccount": {
                     "description": "Kubernetes service account",
                     "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
                 }
             }
         },
@@ -3902,9 +3920,6 @@ var doc = `{
                     "description": "Number of available pods",
                     "type": "integer"
                 },
-                "createdAt": {
-                    "type": "string"
-                },
                 "deployment": {
                     "description": "The model k8s deployment name",
                     "type": "string"
@@ -3932,9 +3947,6 @@ var doc = `{
                 "state": {
                     "description": "The state of a model \n  \"Processing\" - A model was not deployed. Because some parameters of the\n                 custom resource are wrong. For example, there is not a model\n                 image in a Docker registry.\n  \"Ready\" - A model was deployed successfully.",
                     "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
                 }
             }
         },
@@ -3995,9 +4007,6 @@ var doc = `{
         "ModelPackagingStatus": {
             "type": "object",
             "properties": {
-                "createdAt": {
-                    "type": "string"
-                },
                 "exitCode": {
                     "description": "Pod exit code",
                     "type": "integer"
@@ -4024,9 +4033,6 @@ var doc = `{
                 "state": {
                     "description": "Model Packaging State",
                     "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
                 }
             }
         },
@@ -4053,9 +4059,6 @@ var doc = `{
         "ModelRouteStatus": {
             "type": "object",
             "properties": {
-                "createdAt": {
-                    "type": "string"
-                },
                 "edgeUrl": {
                     "description": "Full url with prefix to a model deployment service",
                     "type": "string"
@@ -4063,9 +4066,6 @@ var doc = `{
                 "state": {
                     "description": "State of Model Route",
                     "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
                 }
             }
         },
@@ -4168,9 +4168,6 @@ var doc = `{
                         "$ref": "#/definitions/TrainingResult"
                     }
                 },
-                "createdAt": {
-                    "type": "string"
-                },
                 "exitCode": {
                     "description": "Pod exit code",
                     "type": "integer"
@@ -4190,9 +4187,6 @@ var doc = `{
                 "state": {
                     "description": "Model Packaging State",
                     "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
                 }
             }
         },
@@ -4210,15 +4204,7 @@ var doc = `{
             }
         },
         "PackagingIntegrationStatus": {
-            "type": "object",
-            "properties": {
-                "createdAt": {
-                    "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
-                }
-            }
+            "type": "object"
         },
         "RemoteModelSource": {
             "type": "object",
@@ -4317,15 +4303,7 @@ var doc = `{
             }
         },
         "ToolchainIntegrationStatus": {
-            "type": "object",
-            "properties": {
-                "createdAt": {
-                    "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
-                }
-            }
+            "type": "object"
         },
         "TrainingResult": {
             "type": "object",
diff --git a/packages/operator/docs/swagger.json b/packages/operator/docs/swagger.json
index c6e6974fb..ae2270211 100644
--- a/packages/operator/docs/swagger.json
+++ b/packages/operator/docs/swagger.json
@@ -3204,6 +3204,10 @@
         "Connection": {
             "type": "object",
             "properties": {
+                "createdAt": {
+                    "description": "CreatedAt",
+                    "type": "string"
+                },
                 "id": {
                     "description": "Connection id",
                     "type": "string"
@@ -3217,6 +3221,10 @@
                     "description": "Connection status",
                     "type": "object",
                     "$ref": "#/definitions/ConnectionStatus"
+                },
+                "updatedAt": {
+                    "description": "UpdatedAt",
+                    "type": "string"
                 }
             }
         },
@@ -3504,6 +3512,10 @@
         "PackagingIntegration": {
             "type": "object",
             "properties": {
+                "createdAt": {
+                    "description": "CreatedAt",
+                    "type": "string"
+                },
                 "id": {
                     "description": "Packaging integration id",
                     "type": "string"
@@ -3517,6 +3529,10 @@
                     "description": "Packaging integration status",
                     "type": "object",
                     "$ref": "#/definitions/PackagingIntegrationStatus"
+                },
+                "updatedAt": {
+                    "description": "UpdatedAt",
+                    "type": "string"
                 }
             }
         },
@@ -3668,6 +3684,10 @@
         "ToolchainIntegration": {
             "type": "object",
             "properties": {
+                "createdAt": {
+                    "description": "CreatedAt",
+                    "type": "string"
+                },
                 "id": {
                     "description": "Toolchain integration id",
                     "type": "string"
@@ -3681,6 +3701,10 @@
                     "description": "Toolchain integration status",
                     "type": "object",
                     "$ref": "#/definitions/ToolchainIntegrationStatus"
+                },
+                "updatedAt": {
+                    "description": "UpdatedAt",
+                    "type": "string"
                 }
             }
         },
@@ -3764,9 +3788,6 @@
         "ConnectionStatus": {
             "type": "object",
             "properties": {
-                "createdAt": {
-                    "type": "string"
-                },
                 "secretName": {
                     "description": "Kubernetes secret name",
                     "type": "string"
@@ -3774,9 +3795,6 @@
                 "serviceAccount": {
                     "description": "Kubernetes service account",
                     "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
                 }
             }
         },
@@ -3886,9 +3904,6 @@
                     "description": "Number of available pods",
                     "type": "integer"
                 },
-                "createdAt": {
-                    "type": "string"
-                },
                 "deployment": {
                     "description": "The model k8s deployment name",
                     "type": "string"
@@ -3916,9 +3931,6 @@
                 "state": {
                     "description": "The state of a model \n  \"Processing\" - A model was not deployed. Because some parameters of the\n                 custom resource are wrong. For example, there is not a model\n                 image in a Docker registry.\n  \"Ready\" - A model was deployed successfully.",
                     "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
                 }
             }
         },
@@ -3979,9 +3991,6 @@
         "ModelPackagingStatus": {
             "type": "object",
             "properties": {
-                "createdAt": {
-                    "type": "string"
-                },
                 "exitCode": {
                     "description": "Pod exit code",
                     "type": "integer"
@@ -4008,9 +4017,6 @@
                 "state": {
                     "description": "Model Packaging State",
                     "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
                 }
             }
         },
@@ -4037,9 +4043,6 @@
         "ModelRouteStatus": {
             "type": "object",
             "properties": {
-                "createdAt": {
-                    "type": "string"
-                },
                 "edgeUrl": {
                     "description": "Full url with prefix to a model deployment service",
                     "type": "string"
@@ -4047,9 +4050,6 @@
                 "state": {
                     "description": "State of Model Route",
                     "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
                 }
             }
         },
@@ -4152,9 +4152,6 @@
                         "$ref": "#/definitions/TrainingResult"
                     }
                 },
-                "createdAt": {
-                    "type": "string"
-                },
                 "exitCode": {
                     "description": "Pod exit code",
                     "type": "integer"
@@ -4174,9 +4171,6 @@
                 "state": {
                     "description": "Model Packaging State",
                     "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
                 }
             }
         },
@@ -4194,15 +4188,7 @@
             }
         },
         "PackagingIntegrationStatus": {
-            "type": "object",
-            "properties": {
-                "createdAt": {
-                    "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
-                }
-            }
+            "type": "object"
         },
         "RemoteModelSource": {
             "type": "object",
@@ -4301,15 +4287,7 @@
             }
         },
         "ToolchainIntegrationStatus": {
-            "type": "object",
-            "properties": {
-                "createdAt": {
-                    "type": "string"
-                },
-                "updatedAt": {
-                    "type": "string"
-                }
-            }
+            "type": "object"
         },
         "TrainingResult": {
             "type": "object",
diff --git a/packages/operator/docs/swagger.yaml b/packages/operator/docs/swagger.yaml
index 665719cf2..1fa1d0213 100644
--- a/packages/operator/docs/swagger.yaml
+++ b/packages/operator/docs/swagger.yaml
@@ -85,8 +85,7 @@ definitions:
         description: Message is any message from runtime service about status of InferenceJob
         type: string
       podName:
-        description: PodName is a name of Pod in Kubernetes that is running under
-          the hood of InferenceJob
+        description: PodName is a name of Pod in Kubernetes that is running under the hood of InferenceJob
         type: string
       reason:
         description: |-
@@ -100,8 +99,7 @@ definitions:
   InferenceService:
     properties:
       createdAt:
-        description: When resource was created. Managed by system. Cannot be overridden
-          by User
+        description: When resource was created. Managed by system. Cannot be overridden by User
         type: string
       id:
         type: string
@@ -112,8 +110,7 @@ definitions:
         $ref: '#/definitions/InferenceServiceStatus'
         type: object
       updatedAt:
-        description: When resource was updated. Managed by system. Cannot be overridden
-          by User
+        description: When resource was updated. Managed by system. Cannot be overridden by User
         type: string
     type: object
   InferenceServiceSpec:
@@ -189,15 +186,13 @@ definitions:
     properties:
       webhook:
         $ref: '#/definitions/PredictorWebhookTrigger'
-        description: Webhook provides a REST API to execute InferenceJob that correspond
-          to this service
+        description: Webhook provides a REST API to execute InferenceJob that correspond to this service
         type: object
     type: object
   PredictorWebhookTrigger:
     properties:
       enabled:
-        description: Enabled. If True then it possible to run InferenceJob by creating
-          it using REST API
+        description: Enabled. If True then it possible to run InferenceJob by creating it using REST API
         type: boolean
     type: object
   APIBackendConfig:
@@ -252,8 +247,7 @@ definitions:
         description: Enable batch API/operator
         type: boolean
       namespace:
-        description: Kubernetes namespace, where BatchInferenceService and BatchInferenceJob
-          will be created
+        description: Kubernetes namespace, where BatchInferenceService and BatchInferenceJob will be created
         type: string
       nodePools:
         description: Node pools to run batch jobs
@@ -264,8 +258,7 @@ definitions:
         description: RClone image that will be used to sync data with object storage
         type: string
       serviceAccountName:
-        description: Specifies a ServiceAccount object that provides custom credentials
-          for executing the TaskRun
+        description: Specifies a ServiceAccount object that provides custom credentials for executing the TaskRun
         type: string
       timeout:
         description: Timeout for full batch process
@@ -293,8 +286,7 @@ definitions:
         description: Database connection string
         type: string
       externalUrls:
-        description: 'The collection of external urls, for example: metrics, edge,
-          service catalog and so on'
+        description: 'The collection of external urls, for example: metrics, edge, service catalog and so on'
         items:
           $ref: '#/definitions/ExternalUrl'
         type: array
@@ -383,8 +375,7 @@ definitions:
   ExternalUrl:
     properties:
       imageUrl:
-        description: Optional link to an image which represents a type of the resource,
-          for example the logo of Grafana
+        description: Optional link to an image which represents a type of the resource, for example the logo of Grafana
         type: string
       name:
         description: Human-readable name
@@ -408,8 +399,7 @@ definitions:
   ModelDeploymentConfig:
     properties:
       defaultDockerPullConnName:
-        description: Default connection ID which will be used if a user doesn't specify
-          it in a model deployment
+        description: Default connection ID which will be used if a user doesn't specify it in a model deployment
         type: string
       defaultResources:
         $ref: '#/definitions/ResourceRequirements'
@@ -588,24 +578,19 @@ definitions:
         description: BaseURL is a prefix to service catalog web server endpoints
         type: string
       debug:
-        description: 'enabled Debug increase logger verbosity and format. Default:
-          false'
+        description: 'enabled Debug increase logger verbosity and format. Default: false'
         type: boolean
       edgeHost:
-        description: ServiceCatalog set EdgeHost as Host header in requests to ML
-          servers
+        description: ServiceCatalog set EdgeHost as Host header in requests to ML servers
         type: string
       edgeURL:
-        description: ServiceCatalog uses EdgeURL to call MLServer by adding ModelRoute
-          prefix to EdgeURL path
+        description: ServiceCatalog uses EdgeURL to call MLServer by adding ModelRoute prefix to EdgeURL path
         type: string
       fetchTimeout:
-        description: FetchTimeout configures how often new events will be fetched.
-          Default 5 seconds.
+        description: FetchTimeout configures how often new events will be fetched. Default 5 seconds.
         type: integer
       workersCount:
-        description: 'WorkersCount configures how many workers will process events.
-          Default: 4'
+        description: 'WorkersCount configures how many workers will process events. Default: 4'
         type: integer
     type: object
   TrainerConfig:
@@ -654,6 +639,9 @@ definitions:
     type: object
   Connection:
     properties:
+      createdAt:
+        description: CreatedAt
+        type: string
       id:
         description: Connection id
         type: string
@@ -665,6 +653,9 @@ definitions:
         $ref: '#/definitions/ConnectionStatus'
         description: Connection status
         type: object
+      updatedAt:
+        description: UpdatedAt
+        type: string
     type: object
   ModelDeployment:
     properties:
@@ -847,15 +838,13 @@ definitions:
     properties:
       arguments:
         additionalProperties: true
-        description: List of arguments. This parameter depends on the specific packaging
-          integration
+        description: List of arguments. This parameter depends on the specific packaging integration
         type: object
       artifactName:
         description: Training output artifact name
         type: string
       image:
-        description: Image name. Packaging integration image will be used if this
-          parameters is missed
+        description: Image name. Packaging integration image will be used if this parameters is missed
         type: string
       integrationName:
         description: Packaging integration ID
@@ -877,8 +866,7 @@ definitions:
           The same format like k8s uses for pod resources.
         type: object
       targets:
-        description: List of targets. This parameter depends on the specific packaging
-          integration
+        description: List of targets. This parameter depends on the specific packaging integration
         items:
           $ref: '#/definitions/Target'
         type: array
@@ -895,6 +883,9 @@ definitions:
     type: object
   PackagingIntegration:
     properties:
+      createdAt:
+        description: CreatedAt
+        type: string
       id:
         description: Packaging integration id
         type: string
@@ -906,6 +897,9 @@ definitions:
         $ref: '#/definitions/PackagingIntegrationStatus'
         description: Packaging integration status
         type: object
+      updatedAt:
+        description: UpdatedAt
+        type: string
     type: object
   PackagingIntegrationSpec:
     properties:
@@ -920,8 +914,7 @@ definitions:
         type: boolean
       schema:
         $ref: '#/definitions/Schema'
-        description: Schema which describes targets and arguments for specific packaging
-          integration
+        description: Schema which describes targets and arguments for specific packaging integration
         type: object
     type: object
   Parameter:
@@ -1015,6 +1008,9 @@ definitions:
     type: object
   ToolchainIntegration:
     properties:
+      createdAt:
+        description: CreatedAt
+        type: string
       id:
         description: Toolchain integration id
         type: string
@@ -1026,6 +1022,9 @@ definitions:
         $ref: '#/definitions/ToolchainIntegrationStatus'
         description: Toolchain integration status
         type: object
+      updatedAt:
+        description: UpdatedAt
+        type: string
     type: object
   UserInfo:
     properties:
@@ -1090,16 +1089,12 @@ definitions:
     type: object
   ConnectionStatus:
     properties:
-      createdAt:
-        type: string
       secretName:
         description: Kubernetes secret name
         type: string
       serviceAccount:
         description: Kubernetes service account
         type: string
-      updatedAt:
-        type: string
     type: object
   DataBindingDir:
     properties:
@@ -1142,19 +1137,16 @@ definitions:
         description: Model Docker image
         type: string
       imagePullConnID:
-        description: If pulling of your image requires authorization, then you should
-          specify the connection id
+        description: If pulling of your image requires authorization, then you should specify the connection id
         type: string
       livenessProbeInitialDelay:
         description: Initial delay for liveness probe of model pod
         type: integer
       maxReplicas:
-        description: Maximum number of pods for model. By default the max replicas
-          parameter equals 1.
+        description: Maximum number of pods for model. By default the max replicas parameter equals 1.
         type: integer
       minReplicas:
-        description: Minimum number of pods for model. By default the min replicas
-          parameter equals 0.
+        description: Minimum number of pods for model. By default the min replicas parameter equals 0.
         type: integer
       nodeSelector:
         additionalProperties:
@@ -1182,8 +1174,6 @@ definitions:
       availableReplicas:
         description: Number of available pods
         type: integer
-      createdAt:
-        type: string
       deployment:
         description: The model k8s deployment name
         type: string
@@ -1212,8 +1202,6 @@ definitions:
                            image in a Docker registry.
             "Ready" - A model was deployed successfully.
         type: string
-      updatedAt:
-        type: string
     type: object
   ModelDeploymentTarget:
     properties:
@@ -1246,8 +1234,7 @@ definitions:
   ModelPackagingResult:
     properties:
       name:
-        description: Name of a result. It can be docker image, path to s3 artifact
-          and so on
+        description: Name of a result. It can be docker image, path to s3 artifact and so on
         type: string
       value:
         description: Specific value
@@ -1255,8 +1242,6 @@ definitions:
     type: object
   ModelPackagingStatus:
     properties:
-      createdAt:
-        type: string
       exitCode:
         description: Pod exit code
         type: integer
@@ -1277,8 +1262,6 @@ definitions:
       state:
         description: Model Packaging State
         type: string
-      updatedAt:
-        type: string
     type: object
   ModelRouteSpec:
     properties:
@@ -1301,16 +1284,12 @@ definitions:
     type: object
   ModelRouteStatus:
     properties:
-      createdAt:
-        type: string
       edgeUrl:
         description: Full url with prefix to a model deployment service
         type: string
       state:
         description: State of Model Route
         type: string
-      updatedAt:
-        type: string
     type: object
   ModelSource:
     properties:
@@ -1339,12 +1318,10 @@ definitions:
           $ref: '#/definitions/DataBindingDir'
         type: array
       entrypoint:
-        description: Model training file. It can be python\bash script or jupiter
-          notebook
+        description: Model training file. It can be python\bash script or jupiter notebook
         type: string
       envs:
-        description: Custom environment variables that should be set before entrypoint
-          invocation.
+        description: Custom environment variables that should be set before entrypoint invocation.
         items:
           $ref: '#/definitions/EnvironmentVariable'
         type: array
@@ -1390,8 +1367,6 @@ definitions:
         items:
           $ref: '#/definitions/TrainingResult'
         type: array
-      createdAt:
-        type: string
       exitCode:
         description: Pod exit code
         type: integer
@@ -1407,8 +1382,6 @@ definitions:
       state:
         description: Model Packaging State
         type: string
-      updatedAt:
-        type: string
     type: object
   ObjectStorage:
     properties:
@@ -1420,11 +1393,6 @@ definitions:
         type: string
     type: object
   PackagingIntegrationStatus:
-    properties:
-      createdAt:
-        type: string
-      updatedAt:
-        type: string
     type: object
   RemoteModelSource:
     properties:
@@ -1493,11 +1461,6 @@ definitions:
         type: string
     type: object
   ToolchainIntegrationStatus:
-    properties:
-      createdAt:
-        type: string
-      updatedAt:
-        type: string
     type: object
   TrainingResult:
     properties:
diff --git a/packages/operator/go.mod b/packages/operator/go.mod
index bd13c78e0..5b5c6045d 100644
--- a/packages/operator/go.mod
+++ b/packages/operator/go.mod
@@ -43,6 +43,7 @@ require (
 	github.com/swaggo/swag v1.5.0
 	github.com/tektoncd/pipeline v0.13.1-0.20200625065359-44f22a067b75
 	github.com/tinylib/msgp v1.1.0 // indirect
+	github.com/vektra/mockery/v2 v2.7.5 // indirect
 	github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b // indirect
 	github.com/xeipuuv/gojsonschema v1.1.0
 	github.com/zsais/go-gin-prometheus v0.1.0
diff --git a/packages/operator/go.sum b/packages/operator/go.sum
index f178cc427..a36f7d451 100644
--- a/packages/operator/go.sum
+++ b/packages/operator/go.sum
@@ -1531,6 +1531,9 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
+github.com/rs/zerolog v1.18.0 h1:CbAm3kP2Tptby1i9sYy2MGRg0uxIN9cyDb59Ys7W8z8=
+github.com/rs/zerolog v1.18.0/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I=
 github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
 github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@@ -1721,6 +1724,9 @@ github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44
 github.com/vdemeester/k8s-pkg-credentialprovider v1.13.12-1/go.mod h1:Fko0rTxEtDW2kju5Ky7yFJNS3IcNvW8IPsp4/e9oev0=
 github.com/vdemeester/k8s-pkg-credentialprovider v1.17.4/go.mod h1:inCTmtUdr5KJbreVojo06krnTgaeAz/Z7lynpPk/Q2c=
 github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
+github.com/vektra/mockery v1.1.2 h1:uc0Yn67rJpjt8U/mAZimdCKn9AeA97BOkjpmtBSlfP4=
+github.com/vektra/mockery/v2 v2.7.5 h1:6j0fRHgYCU/BBeWK973edLaGedt3TnH/IMJNjxcd8m0=
+github.com/vektra/mockery/v2 v2.7.5/go.mod h1:2gU4Cf/f8YyC8oEaSXfCnZBMxMjMl/Ko205rlP0fO90=
 github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3 h1:zMsHhfK9+Wdl1F7sIKLyx3wrOFofpb3rWFbA4HgcK5k=
 github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3/go.mod h1:R0Gbuw7ElaGSLOZUSwBm/GgVwMd30jWxBDdAyMOeTuc=
 github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
@@ -1763,6 +1769,7 @@ github.com/zeebo/errs v1.2.2 h1:5NFypMTuSdoySVTqlNs1dEoU21QVamMQJxW/Fii5O7g=
 github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
 github.com/zeebo/float16 v0.1.0/go.mod h1:fssGvvXu+XS8MH57cKmyrLB/cqioYeYX/2mXCN3a5wo=
 github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54/go.mod h1:EI8LcOBDlSL3POyqwC1eJhOYlMBMidES+613EtmmT5w=
+github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
 github.com/zsais/go-gin-prometheus v0.1.0 h1:bkLv1XCdzqVgQ36ScgRi09MA2UC1t3tAB6nsfErsGO4=
 github.com/zsais/go-gin-prometheus v0.1.0/go.mod h1:Slirjzuz8uM8Cw0jmPNqbneoqcUtY2GGjn2bEd4NRLY=
 go.etcd.io/bbolt v1.3.1-etcd.7/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -2105,6 +2112,7 @@ golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDq
 golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
 golang.org/x/tools v0.0.0-20190807223507-b346f7fd45de/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -2144,6 +2152,7 @@ golang.org/x/tools v0.0.0-20200303214625-2b0b585e22fe/go.mod h1:o4KQGtdN14AW+yjs
 golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
 golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
 golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200323144430-8dcfad9e016e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
 golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
 golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
 golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
diff --git a/packages/operator/pkg/apis/connection/connection.go b/packages/operator/pkg/apis/connection/connection.go
index 140e4b01f..f8f8d6549 100644
--- a/packages/operator/pkg/apis/connection/connection.go
+++ b/packages/operator/pkg/apis/connection/connection.go
@@ -20,6 +20,7 @@ import (
 	"encoding/base64"
 	"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
 	"go.uber.org/multierr"
+	"time"
 )
 
 const (
@@ -53,6 +54,10 @@ func init() {
 type Connection struct {
 	// Connection id
 	ID string `json:"id"`
+	// CreatedAt
+	CreatedAt time.Time `json:"createdAt,omitempty"`
+	// UpdatedAt
+	UpdatedAt time.Time `json:"updatedAt,omitempty"`
 	// Connection specification
 	Spec v1alpha1.ConnectionSpec `json:"spec"`
 	// Connection status
diff --git a/packages/operator/pkg/apis/packaging/packaging_integration.go b/packages/operator/pkg/apis/packaging/packaging_integration.go
index 813c4a553..51cf98191 100644
--- a/packages/operator/pkg/apis/packaging/packaging_integration.go
+++ b/packages/operator/pkg/apis/packaging/packaging_integration.go
@@ -21,11 +21,16 @@ import (
 	"encoding/json"
 	"errors"
 	"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
+	"time"
 )
 
 type PackagingIntegration struct {
 	// Packaging integration id
 	ID string `json:"id"`
+	// CreatedAt
+	CreatedAt time.Time `json:"createdAt,omitempty"`
+	// UpdatedAt
+	UpdatedAt time.Time `json:"updatedAt,omitempty"`
 	// Packaging integration specification
 	Spec PackagingIntegrationSpec `json:"spec,omitempty"`
 	// Packaging integration status
diff --git a/packages/operator/pkg/apis/training/toolchain_integration.go b/packages/operator/pkg/apis/training/toolchain_integration.go
index bf5640af6..4e46b98f7 100644
--- a/packages/operator/pkg/apis/training/toolchain_integration.go
+++ b/packages/operator/pkg/apis/training/toolchain_integration.go
@@ -16,11 +16,18 @@
 
 package training
 
-import "github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
+import (
+	"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
+	"time"
+)
 
 type ToolchainIntegration struct {
 	// Toolchain integration id
 	ID string `json:"id"`
+	// CreatedAt
+	CreatedAt time.Time `json:"createdAt,omitempty"`
+	// UpdatedAt
+	UpdatedAt time.Time `json:"updatedAt,omitempty"`
 	// Toolchain integration specification
 	Spec v1alpha1.ToolchainIntegrationSpec `json:"spec,omitempty"`
 	// Toolchain integration status
diff --git a/packages/operator/pkg/apiserver/routes/v1/collector.go b/packages/operator/pkg/apiserver/routes/v1/collector.go
index e15e7d7ee..70cc524bb 100644
--- a/packages/operator/pkg/apiserver/routes/v1/collector.go
+++ b/packages/operator/pkg/apiserver/routes/v1/collector.go
@@ -21,36 +21,38 @@ import (
 	"errors"
 	"github.com/gin-gonic/gin"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes"
+	job_routes "github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/batch/job"
+	service_routes "github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/batch/service"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/configuration"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/connection"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/deployment"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/packaging"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/training"
 	userinfo "github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/user"
-	service_routes "github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/batch/service"
-	job_routes "github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/batch/job"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
+	pack_kube_client "github.com/odahu/odahu-flow/packages/operator/pkg/kubeclient/packagingclient"
+	train_kube_client "github.com/odahu/odahu-flow/packages/operator/pkg/kubeclient/trainingclient"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection/kubernetes"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection/memory"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection/vault"
+	batch_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/batch"
 	conn_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/connection"
 	md_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/deployment"
+	mp_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/packaging"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/service/packaging_integration"
 	mr_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/route"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/service/toolchain"
 	mt_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/training"
-	mp_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/packaging"
-	batch_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/batch"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/utils"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
-	pack_kube_client "github.com/odahu/odahu-flow/packages/operator/pkg/kubeclient/packagingclient"
-	train_kube_client "github.com/odahu/odahu-flow/packages/operator/pkg/kubeclient/trainingclient"
 
+	batch_repo "github.com/odahu/odahu-flow/packages/operator/pkg/repository/batch/postgres"
 	conn_repo_type "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
 	deploy_repo "github.com/odahu/odahu-flow/packages/operator/pkg/repository/deployment/postgres"
-	route_repo "github.com/odahu/odahu-flow/packages/operator/pkg/repository/route/postgres"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/repository/outbox"
 	pack_repo "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging/postgres"
+	route_repo "github.com/odahu/odahu-flow/packages/operator/pkg/repository/route/postgres"
 	train_repo "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training/postgres"
-	batch_repo "github.com/odahu/odahu-flow/packages/operator/pkg/repository/batch/postgres"
-	"github.com/odahu/odahu-flow/packages/operator/pkg/repository/outbox"
 )
 
 func SetupV1Routes(routeGroup *gin.RouterGroup, kubeMgr manager.Manager, db *sql.DB, cfg config.Config) (err error) {
@@ -77,10 +79,12 @@ func SetupV1Routes(routeGroup *gin.RouterGroup, kubeMgr manager.Manager, db *sql
 		return errors.New("unexpect connection repository type")
 	}
 
-
 	toolchainRepo := train_repo.ToolchainRepo{DB: db}
+	toolchainService := toolchain.NewService(toolchainRepo)
+
 	trainRepo := train_repo.TrainingRepo{DB: db}
 	piRepo := pack_repo.PackagingIntegrationRepository{DB: db}
+	piService := packaging_integration.NewService(&piRepo)
 	packRepo := pack_repo.PackagingRepo{DB: db}
 	deployRepo := deploy_repo.DeploymentRepo{DB: db}
 	routeRepo := route_repo.RouteRepo{DB: db}
@@ -108,7 +112,6 @@ func SetupV1Routes(routeGroup *gin.RouterGroup, kubeMgr manager.Manager, db *sql
 	batchServiceService := batch_service.NewInferenceServiceService(batchServiceRepo)
 	batchJobService := batch_service.NewJobService(batchJobRepo, batchServiceRepo, connService)
 
-
 	connection.ConfigureRoutes(routeGroup, connService, utils.EvaluatePublicKey, cfg.Connection)
 
 	mdEventGetter := outbox.DeploymentEventGetter{DB: db}
@@ -119,9 +122,9 @@ func SetupV1Routes(routeGroup *gin.RouterGroup, kubeMgr manager.Manager, db *sql
 	packagingRouteGroup := routeGroup.Group("", routes.DisableAPIMiddleware(cfg.Packaging.Enabled))
 	packaging.ConfigureRoutes(
 		packagingRouteGroup, packKubeClient, packService,
-		piRepo, connRepository, cfg.Packaging, cfg.Common.ResourceGPUName,
+		piService, connRepository, cfg.Packaging, cfg.Common.ResourceGPUName,
 	)
-	packaging.ConfigurePiRoutes(packagingRouteGroup, piRepo)
+	packaging.ConfigurePiRoutes(packagingRouteGroup, piService)
 
 	trainingRouteGroup := routeGroup.Group("", routes.DisableAPIMiddleware(cfg.Training.Enabled))
 
@@ -129,10 +132,10 @@ func SetupV1Routes(routeGroup *gin.RouterGroup, kubeMgr manager.Manager, db *sql
 		trainingRouteGroup,
 		cfg.Training,
 		cfg.Common.ResourceGPUName,
-		trainService, toolchainRepo, connRepository, trainKubeClient)
+		trainService, toolchainService, connRepository, trainKubeClient)
 
 	training.ConfigureToolchainRoutes(
-		trainingRouteGroup, toolchainRepo,
+		trainingRouteGroup, toolchainService,
 	)
 
 	configuration.ConfigureRoutes(routeGroup, cfg)
diff --git a/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_validation.go b/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_validation.go
index 15e93b3d3..e68264724 100644
--- a/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_validation.go
+++ b/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_validation.go
@@ -28,7 +28,6 @@ import (
 	odahuflowv1alpha1 "github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/packaging"
 	conn_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
-	mp_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/repository/util/kubernetes"
 	"github.com/xeipuuv/gojsonschema"
 	"go.uber.org/multierr"
@@ -44,21 +43,25 @@ const (
 	UnknownNodeSelector                  = "node selector %v is not presented in ODAHU config"
 )
 
+type packagingIntegrationGetter interface {
+	GetPackagingIntegration(id string) (*packaging.PackagingIntegration, error)
+}
+
 type MpValidator struct {
-	piRepo          mp_repository.PackagingIntegrationRepository
+	piService       packagingIntegrationGetter
 	connRepo        conn_repository.Repository
 	gpuResourceName string
 	packagingConfig config.ModelPackagingConfig
 }
 
 func NewMpValidator(
-	piRepo mp_repository.PackagingIntegrationRepository,
+	piService packagingIntegrationGetter,
 	connRepo conn_repository.Repository,
 	packagingConfig config.ModelPackagingConfig,
 	gpuResourceName string,
 ) *MpValidator {
 	return &MpValidator{
-		piRepo:          piRepo,
+		piService:       piService,
 		connRepo:        connRepo,
 		packagingConfig: packagingConfig,
 		gpuResourceName: gpuResourceName,
@@ -68,7 +71,7 @@ func NewMpValidator(
 // ValidateAndSetDefaultsPIRequired validates ModelPackaging fields using a corresponding PackagingIntegration
 func (mpv *MpValidator) ValidateAndSetDefaultsPIRequired(mp *packaging.ModelPackaging) (err error) {
 
-	pi, piErr := mpv.piRepo.GetPackagingIntegration(mp.Spec.IntegrationName)
+	pi, piErr := mpv.piService.GetPackagingIntegration(mp.Spec.IntegrationName)
 	switch {
 	case piErr == nil:
 		if len(mp.Spec.Image) == 0 {
diff --git a/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration.go b/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration.go
index e3a19bd9c..dedcb4f48 100644
--- a/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration.go
+++ b/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration.go
@@ -22,7 +22,6 @@ import (
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/packaging"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/errors"
-	mp_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
 	httputil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/httputil"
 	"net/http"
@@ -44,9 +43,17 @@ var (
 	emptyCache = map[string]int{}
 )
 
+type packagingIntegrationService interface {
+	GetPackagingIntegration(id string) (*packaging.PackagingIntegration, error)
+	GetPackagingIntegrationList(options ...filter.ListOption) ([]packaging.PackagingIntegration, error)
+	CreatePackagingIntegration(md *packaging.PackagingIntegration) error
+	UpdatePackagingIntegration(md *packaging.PackagingIntegration) error
+	DeletePackagingIntegration(id string) error
+}
+
 type PackagingIntegrationController struct {
-	repository mp_repository.PackagingIntegrationRepository
-	validator  *PiValidator
+	service   packagingIntegrationService
+	validator *PiValidator
 }
 
 // @Summary Get a PackagingIntegration
@@ -63,7 +70,7 @@ type PackagingIntegrationController struct {
 func (pic *PackagingIntegrationController) getPackagingIntegration(c *gin.Context) {
 	piID := c.Param(IDPiURLParam)
 
-	pi, err := pic.repository.GetPackagingIntegration(piID)
+	pi, err := pic.service.GetPackagingIntegration(piID)
 	if err != nil {
 		logPi.Error(err, fmt.Sprintf("Retrieving %s packaging integration", piID))
 		c.AbortWithStatusJSON(errors.CalculateHTTPStatusCode(err), httputil.HTTPResult{Message: err.Error()})
@@ -93,7 +100,7 @@ func (pic *PackagingIntegrationController) getAllPackagingIntegrations(c *gin.Co
 		return
 	}
 
-	piList, err := pic.repository.GetPackagingIntegrationList(
+	piList, err := pic.service.GetPackagingIntegrationList(
 		filter.Size(size),
 		filter.Page(page),
 	)
@@ -133,7 +140,7 @@ func (pic *PackagingIntegrationController) createPackagingIntegration(c *gin.Con
 		return
 	}
 
-	if err := pic.repository.CreatePackagingIntegration(&pi); err != nil {
+	if err := pic.service.CreatePackagingIntegration(&pi); err != nil {
 		logPi.Error(err, fmt.Sprintf("Creation of the packaging integration: %+v", pi))
 		c.AbortWithStatusJSON(errors.CalculateHTTPStatusCode(err), httputil.HTTPResult{Message: err.Error()})
 
@@ -170,7 +177,7 @@ func (pic *PackagingIntegrationController) updatePackagingIntegration(c *gin.Con
 		return
 	}
 
-	if err := pic.repository.UpdatePackagingIntegration(&pi); err != nil {
+	if err := pic.service.UpdatePackagingIntegration(&pi); err != nil {
 		logPi.Error(err, fmt.Sprintf("Update of the packaging integration: %+v", pi))
 		c.AbortWithStatusJSON(errors.CalculateHTTPStatusCode(err), httputil.HTTPResult{Message: err.Error()})
 
@@ -194,7 +201,7 @@ func (pic *PackagingIntegrationController) updatePackagingIntegration(c *gin.Con
 func (pic *PackagingIntegrationController) deletePackagingIntegration(c *gin.Context) {
 	piID := c.Param(IDPiURLParam)
 
-	if err := pic.repository.DeletePackagingIntegration(piID); err != nil {
+	if err := pic.service.DeletePackagingIntegration(piID); err != nil {
 		logPi.Error(err, fmt.Sprintf("Deletion of %s packaging integration is failed", piID))
 		c.AbortWithStatusJSON(errors.CalculateHTTPStatusCode(err), httputil.HTTPResult{Message: err.Error()})
 
diff --git a/packages/operator/pkg/apiserver/routes/v1/packaging/pi_routes.go b/packages/operator/pkg/apiserver/routes/v1/packaging/pi_routes.go
index 60f180d2b..3d2061b0d 100644
--- a/packages/operator/pkg/apiserver/routes/v1/packaging/pi_routes.go
+++ b/packages/operator/pkg/apiserver/routes/v1/packaging/pi_routes.go
@@ -18,14 +18,13 @@ package packaging
 
 import (
 	"github.com/gin-gonic/gin"
-	mp_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging"
 )
 
-func ConfigurePiRoutes(routeGroup *gin.RouterGroup, piRepository mp_repository.PackagingIntegrationRepository) {
+func ConfigurePiRoutes(routeGroup *gin.RouterGroup, piService packagingIntegrationService) {
 
 	piController := &PackagingIntegrationController{
-		repository: piRepository,
-		validator:  NewPiValidator(),
+		service:   piService,
+		validator: NewPiValidator(),
 	}
 
 	routeGroup.GET(GetPackagingIntegrationURL, piController.getPackagingIntegration)
diff --git a/packages/operator/pkg/apiserver/routes/v1/packaging/routes.go b/packages/operator/pkg/apiserver/routes/v1/packaging/routes.go
index e8156a251..efe18f239 100644
--- a/packages/operator/pkg/apiserver/routes/v1/packaging/routes.go
+++ b/packages/operator/pkg/apiserver/routes/v1/packaging/routes.go
@@ -19,9 +19,8 @@ package packaging
 import (
 	"github.com/gin-gonic/gin"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
-	conn_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
-	mp_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging"
 	mp_kube_client "github.com/odahu/odahu-flow/packages/operator/pkg/kubeclient/packagingclient"
+	conn_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
 	mp_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/packaging"
 )
 
@@ -29,7 +28,7 @@ func ConfigureRoutes(
 	routeGroup *gin.RouterGroup,
 	packKubeClient mp_kube_client.Client,
 	packService mp_service.Service,
-	piRepo mp_repository.PackagingIntegrationRepository,
+	piService packagingIntegrationService,
 	connRepo conn_repository.Repository,
 	config config.ModelPackagingConfig,
 	gpuResourceName string) {
@@ -38,7 +37,7 @@ func ConfigureRoutes(
 		kubeClient:  packKubeClient,
 		packService: packService,
 		validator: NewMpValidator(
-			piRepo,
+			piService,
 			connRepo,
 			config,
 			gpuResourceName,
diff --git a/packages/operator/pkg/apiserver/routes/v1/training/model_training_validation.go b/packages/operator/pkg/apiserver/routes/v1/training/model_training_validation.go
index 5f76c5015..d186e9c74 100644
--- a/packages/operator/pkg/apiserver/routes/v1/training/model_training_validation.go
+++ b/packages/operator/pkg/apiserver/routes/v1/training/model_training_validation.go
@@ -23,7 +23,6 @@ import (
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/training"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
 	conn_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
-	mt_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/repository/util/kubernetes"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/validation"
 	"go.uber.org/multierr"
@@ -53,21 +52,25 @@ var (
 	DefaultArtifactOutputTemplate = "{{ .Name }}-{{ .Version }}-{{ .RandomUUID }}.zip"
 )
 
+type toolchainGetter interface {
+	GetToolchainIntegration(name string) (*training.ToolchainIntegration, error)
+}
+
 type MtValidator struct {
-	mtRepository    mt_repository.ToolchainRepository
+	tiService       toolchainGetter
 	connRepository  conn_repository.Repository
 	gpuResourceName string
 	trainingConfig  config.ModelTrainingConfig
 }
 
 func NewMtValidator(
-	mtRepository mt_repository.ToolchainRepository,
+	tiService toolchainGetter,
 	connRepository conn_repository.Repository,
 	trainingConfig config.ModelTrainingConfig,
 	gpuResourceName string,
 ) *MtValidator {
 	return &MtValidator{
-		mtRepository:    mtRepository,
+		tiService:       tiService,
 		connRepository:  connRepository,
 		trainingConfig:  trainingConfig,
 		gpuResourceName: gpuResourceName,
@@ -150,7 +153,7 @@ func (mtv *MtValidator) validateToolchain(mt *training.ModelTraining) (err error
 		err = multierr.Append(err, errors.New("invalid training toolchain: "+labelErr.Error()))
 	}
 
-	toolchain, k8sErr := mtv.mtRepository.GetToolchainIntegration(mt.Spec.Toolchain)
+	toolchain, k8sErr := mtv.tiService.GetToolchainIntegration(mt.Spec.Toolchain)
 	if k8sErr != nil {
 		err = multierr.Append(err, k8sErr)
 
diff --git a/packages/operator/pkg/apiserver/routes/v1/training/routes.go b/packages/operator/pkg/apiserver/routes/v1/training/routes.go
index 64dd995bb..f13f5f4ea 100644
--- a/packages/operator/pkg/apiserver/routes/v1/training/routes.go
+++ b/packages/operator/pkg/apiserver/routes/v1/training/routes.go
@@ -19,9 +19,8 @@ package training
 import (
 	"github.com/gin-gonic/gin"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
-	conn_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
-	mt_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training"
 	mt_kube_client "github.com/odahu/odahu-flow/packages/operator/pkg/kubeclient/trainingclient"
+	conn_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
 	mt_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/training"
 )
 
@@ -30,7 +29,7 @@ func ConfigureRoutes(
 	config config.ModelTrainingConfig,
 	gpuResourceName string,
 	trainService mt_service.Service,
-	toolchainRepo mt_repository.ToolchainRepository,
+	toolchainService toolchainGetter,
 	connRepo conn_repository.Repository,
 	trainKubeClient mt_kube_client.Client) {
 
@@ -38,7 +37,7 @@ func ConfigureRoutes(
 		trainService: trainService,
 		kubeClient:   trainKubeClient,
 		validator: NewMtValidator(
-			toolchainRepo,
+			toolchainService,
 			connRepo,
 			config,
 			gpuResourceName,
diff --git a/packages/operator/pkg/apiserver/routes/v1/training/ti_routes.go b/packages/operator/pkg/apiserver/routes/v1/training/ti_routes.go
index f80a88ccd..ae94467ca 100644
--- a/packages/operator/pkg/apiserver/routes/v1/training/ti_routes.go
+++ b/packages/operator/pkg/apiserver/routes/v1/training/ti_routes.go
@@ -18,17 +18,16 @@ package training
 
 import (
 	"github.com/gin-gonic/gin"
-	mt_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training"
 )
 
 func ConfigureToolchainRoutes(
 	routeGroup *gin.RouterGroup,
-	mtiRepository mt_repository.ToolchainRepository,
+	tiService toolchainService,
 ) {
 
 	tiController := &ToolchainIntegrationController{
-		repository: mtiRepository,
-		validator:  NewTiValidator(),
+		service:   tiService,
+		validator: NewTiValidator(),
 	}
 
 	routeGroup.GET(GetToolchainIntegrationURL, tiController.getToolchainIntegration)
diff --git a/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration.go b/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration.go
index 1c9c33b3d..081cf693e 100644
--- a/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration.go
+++ b/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration.go
@@ -22,7 +22,6 @@ import (
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/training"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/errors"
-	mt_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
 	httputil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/httputil"
 	"net/http"
@@ -44,9 +43,17 @@ var (
 	emptyCache = map[string]int{}
 )
 
+type toolchainService interface {
+	GetToolchainIntegration(name string) (*training.ToolchainIntegration, error)
+	GetToolchainIntegrationList(options ...filter.ListOption) ([]training.ToolchainIntegration, error)
+	CreateToolchainIntegration(md *training.ToolchainIntegration) error
+	UpdateToolchainIntegration(md *training.ToolchainIntegration) error
+	DeleteToolchainIntegration(name string) error
+}
+
 type ToolchainIntegrationController struct {
-	repository mt_repository.ToolchainRepository
-	validator  *TiValidator
+	service   toolchainService
+	validator *TiValidator
 }
 
 // @Summary Get a ToolchainIntegration
@@ -63,7 +70,7 @@ type ToolchainIntegrationController struct {
 func (tic *ToolchainIntegrationController) getToolchainIntegration(c *gin.Context) {
 	tiID := c.Param(IDTiURLParam)
 
-	ti, err := tic.repository.GetToolchainIntegration(tiID)
+	ti, err := tic.service.GetToolchainIntegration(tiID)
 	if err != nil {
 		logTI.Error(err, fmt.Sprintf("Retrieving %s toolchain integration", tiID))
 		c.AbortWithStatusJSON(errors.CalculateHTTPStatusCode(err), httputil.HTTPResult{Message: err.Error()})
@@ -93,7 +100,7 @@ func (tic *ToolchainIntegrationController) getAllToolchainIntegrations(c *gin.Co
 		return
 	}
 
-	tiList, err := tic.repository.GetToolchainIntegrationList(
+	tiList, err := tic.service.GetToolchainIntegrationList(
 		filter.Size(size),
 		filter.Page(page),
 	)
@@ -133,7 +140,7 @@ func (tic *ToolchainIntegrationController) createToolchainIntegration(c *gin.Con
 		return
 	}
 
-	if err := tic.repository.CreateToolchainIntegration(&ti); err != nil {
+	if err := tic.service.CreateToolchainIntegration(&ti); err != nil {
 		logTI.Error(err, fmt.Sprintf("Creation of toolchain integration: %v", ti))
 		c.AbortWithStatusJSON(errors.CalculateHTTPStatusCode(err), httputil.HTTPResult{Message: err.Error()})
 
@@ -170,7 +177,7 @@ func (tic *ToolchainIntegrationController) updateToolchainIntegration(c *gin.Con
 		return
 	}
 
-	if err := tic.repository.UpdateToolchainIntegration(&ti); err != nil {
+	if err := tic.service.UpdateToolchainIntegration(&ti); err != nil {
 		logTI.Error(err, fmt.Sprintf("Update of toolchain integration: %v", ti))
 		c.AbortWithStatusJSON(errors.CalculateHTTPStatusCode(err), httputil.HTTPResult{Message: err.Error()})
 
@@ -194,7 +201,7 @@ func (tic *ToolchainIntegrationController) updateToolchainIntegration(c *gin.Con
 func (tic *ToolchainIntegrationController) deleteToolchainIntegration(c *gin.Context) {
 	tiID := c.Param(IDTiURLParam)
 
-	if err := tic.repository.DeleteToolchainIntegration(tiID); err != nil {
+	if err := tic.service.DeleteToolchainIntegration(tiID); err != nil {
 		logTI.Error(err, fmt.Sprintf("Deletion of %s toolchain integration is failed", tiID))
 		c.AbortWithStatusJSON(errors.CalculateHTTPStatusCode(err), httputil.HTTPResult{Message: err.Error()})
 
diff --git a/packages/operator/pkg/kubeclient/deploymentclient/client.go b/packages/operator/pkg/kubeclient/deploymentclient/client.go
index 89e5c2e89..1f044e410 100644
--- a/packages/operator/pkg/kubeclient/deploymentclient/client.go
+++ b/packages/operator/pkg/kubeclient/deploymentclient/client.go
@@ -10,14 +10,12 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
-	"time"
 )
 
 const (
 	TagKey = "name"
 )
 
-
 var (
 	DefaultMDDeleteOption = metav1.DeletePropagationForeground
 	log                   = logf.Log.WithName("modeldeployment-kube-client")
@@ -44,7 +42,6 @@ func NewClientWithOptions(namespace string, k8sClient client.Client,
 	}
 }
 
-
 func mdTransformToLabels(md *deployment.ModelDeployment) map[string]string {
 	return map[string]string{
 		"roleName": *md.Spec.RoleName,
@@ -157,7 +154,6 @@ func (c *deployClient) UpdateModelDeployment(md *deployment.ModelDeployment) err
 	}
 
 	k8sMD.Spec = md.Spec
-	k8sMD.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
 	k8sMD.ObjectMeta.Labels = mdTransformToLabels(md)
 
 	if err := c.k8sClient.Update(context.TODO(), &k8sMD); err != nil {
@@ -181,9 +177,6 @@ func (c *deployClient) CreateModelDeployment(md *deployment.ModelDeployment) err
 		Spec: md.Spec,
 	}
 
-	k8sMd.Status.CreatedAt = &metav1.Time{Time: time.Now()}
-	k8sMd.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
-
 	if err := c.k8sClient.Create(context.TODO(), k8sMd); err != nil {
 		log.Error(err, "ModelDeployment creation error from k8s", "name", md.ID)
 
@@ -300,7 +293,6 @@ func (c *deployClient) UpdateModelRoute(route *deployment.ModelRoute) error {
 
 	// TODO: think about update, not replacing as for now
 	k8sMR.Spec = route.Spec
-	k8sMR.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
 
 	if err := c.k8sClient.Update(context.TODO(), &k8sMR); err != nil {
 		log.Error(err, "Creation of the route", "name", route.ID)
@@ -322,9 +314,6 @@ func (c *deployClient) CreateModelRoute(route *deployment.ModelRoute) error {
 		Spec: route.Spec,
 	}
 
-	k8sRoute.Status.CreatedAt = &metav1.Time{Time: time.Now()}
-	k8sRoute.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
-
 	if err := c.k8sClient.Create(context.TODO(), k8sRoute); err != nil {
 		log.Error(err, "DataBinding creation error from k8s", "name", route.ID)
 
@@ -335,4 +324,3 @@ func (c *deployClient) CreateModelRoute(route *deployment.ModelRoute) error {
 
 	return nil
 }
-
diff --git a/packages/operator/pkg/kubeclient/packagingclient/client.go b/packages/operator/pkg/kubeclient/packagingclient/client.go
index 8fafa7251..a5499ab84 100644
--- a/packages/operator/pkg/kubeclient/packagingclient/client.go
+++ b/packages/operator/pkg/kubeclient/packagingclient/client.go
@@ -20,18 +20,16 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
-	"k8s.io/client-go/rest"
-	"time"
-
 	"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/packaging"
 	kube_utils "github.com/odahu/odahu-flow/packages/operator/pkg/kubeclient"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/odahuflow"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/utils"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/rest"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
 )
@@ -229,7 +227,6 @@ func (c *packagingK8SClient) UpdateModelPackaging(mp *packaging.ModelPackaging)
 	k8sMp.Status.Reason = nil
 	k8sMp.Status.Message = nil
 	k8sMp.Status.Results = []v1alpha1.ModelPackagingResult{}
-	k8sMp.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
 	k8sMp.ObjectMeta.Labels = updatedK8sMpSpec.Labels
 
 	if err := c.k8sClient.Update(context.TODO(), &k8sMp); err != nil {
@@ -249,9 +246,6 @@ func (c *packagingK8SClient) CreateModelPackaging(mp *packaging.ModelPackaging)
 		return err
 	}
 
-	k8sMp.Status.CreatedAt = &metav1.Time{Time: time.Now()}
-	k8sMp.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
-
 	if err := c.k8sClient.Create(context.TODO(), k8sMp); err != nil {
 		logMP.Error(err, "Model packaging creation error from k8s", "id", mp.ID)
 
diff --git a/packages/operator/pkg/kubeclient/trainingclient/client.go b/packages/operator/pkg/kubeclient/trainingclient/client.go
index a11f53ff5..ef90a6033 100644
--- a/packages/operator/pkg/kubeclient/trainingclient/client.go
+++ b/packages/operator/pkg/kubeclient/trainingclient/client.go
@@ -22,16 +22,14 @@ import (
 	"fmt"
 	"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/training"
-	"github.com/odahu/odahu-flow/packages/operator/pkg/odahuflow"
-	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
 	kube_utils "github.com/odahu/odahu-flow/packages/operator/pkg/kubeclient"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/odahuflow"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/utils"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
 	corev1 "k8s.io/api/core/v1"
-	"k8s.io/client-go/rest"
-	"time"
-
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/rest"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
 )
@@ -244,7 +242,6 @@ func (c *trainingK8SClient) UpdateModelTraining(mt *training.ModelTraining) erro
 	k8sMD.Status.ExitCode = nil
 	k8sMD.Status.Reason = nil
 	k8sMD.Status.Message = nil
-	k8sMD.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
 	k8sMD.ObjectMeta.Labels = mtTransformToLabels(mt)
 
 	if err := c.k8sClient.Update(context.TODO(), &k8sMD); err != nil {
@@ -268,9 +265,6 @@ func (c *trainingK8SClient) CreateModelTraining(mt *training.ModelTraining) erro
 		Spec: mt.Spec,
 	}
 
-	k8sMd.Status.CreatedAt = &metav1.Time{Time: time.Now()}
-	k8sMd.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
-
 	if err := c.k8sClient.Create(context.TODO(), k8sMd); err != nil {
 		logMT.Error(err, "StorageEntity creation error from k8s", "name", mt.ID)
 
diff --git a/packages/operator/pkg/repository/connection/kubernetes/connection.go b/packages/operator/pkg/repository/connection/kubernetes/connection.go
index 3242e8e0d..469e47c2b 100644
--- a/packages/operator/pkg/repository/connection/kubernetes/connection.go
+++ b/packages/operator/pkg/repository/connection/kubernetes/connection.go
@@ -26,7 +26,6 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
-	"time"
 )
 
 var (
@@ -169,7 +168,6 @@ func (kc *k8sConnectionRepository) UpdateConnection(conn *connection.Connection)
 
 	// TODO: think about update, not replacing as for now
 	k8sConn.Spec = conn.Spec
-	k8sConn.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
 	k8sConn.ObjectMeta.Labels = transformToLabels(conn)
 
 	if err := kc.k8sClient.Update(context.TODO(), &k8sConn); err != nil {
@@ -183,7 +181,7 @@ func (kc *k8sConnectionRepository) UpdateConnection(conn *connection.Connection)
 	return nil
 }
 
-func (kc *k8sConnectionRepository) CreateConnection(connection *connection.Connection) error {
+func (kc *k8sConnectionRepository) SaveConnection(connection *connection.Connection) error {
 	conn := &v1alpha1.Connection{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      connection.ID,
@@ -193,9 +191,6 @@ func (kc *k8sConnectionRepository) CreateConnection(connection *connection.Conne
 		Spec: connection.Spec,
 	}
 
-	conn.Status.CreatedAt = &metav1.Time{Time: time.Now()}
-	conn.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
-
 	if err := kc.k8sClient.Create(context.TODO(), conn); err != nil {
 		logC.Error(err, "ConnectionName creation error from k8s", "name", connection.ID)
 
diff --git a/packages/operator/pkg/repository/connection/memory/connection.go b/packages/operator/pkg/repository/connection/memory/connection.go
index a6ac2119d..835e8fc27 100644
--- a/packages/operator/pkg/repository/connection/memory/connection.go
+++ b/packages/operator/pkg/repository/connection/memory/connection.go
@@ -26,12 +26,11 @@ import (
 
 type repository struct {
 	mu *sync.Mutex
-	m map[string]*connection.Connection
+	m  map[string]*connection.Connection
 }
 
-
 // Not for Production! Development / Testing purposes only
-func NewRepository () conn_repository.Repository {
+func NewRepository() conn_repository.Repository {
 	return repository{
 		mu: &sync.Mutex{},
 		m:  make(map[string]*connection.Connection),
@@ -72,7 +71,7 @@ func (r repository) UpdateConnection(connection *connection.Connection) error {
 	return nil
 }
 
-func (r repository) CreateConnection(connection *connection.Connection) error {
+func (r repository) SaveConnection(connection *connection.Connection) error {
 	r.mu.Lock()
 	defer r.mu.Unlock()
 	r.m[connection.ID] = connection
diff --git a/packages/operator/pkg/repository/connection/repository.go b/packages/operator/pkg/repository/connection/repository.go
index 504b21cd3..041eb0d7c 100644
--- a/packages/operator/pkg/repository/connection/repository.go
+++ b/packages/operator/pkg/repository/connection/repository.go
@@ -29,7 +29,7 @@ type Repository interface {
 	GetConnectionList(options ...ListOption) ([]connection.Connection, error)
 	DeleteConnection(id string) error
 	UpdateConnection(connection *connection.Connection) error
-	CreateConnection(connection *connection.Connection) error
+	SaveConnection(connection *connection.Connection) error
 }
 
 type Filter struct {
diff --git a/packages/operator/pkg/repository/connection/vault/connection.go b/packages/operator/pkg/repository/connection/vault/connection.go
index d80e29be4..89465402f 100644
--- a/packages/operator/pkg/repository/connection/vault/connection.go
+++ b/packages/operator/pkg/repository/connection/vault/connection.go
@@ -3,17 +3,14 @@ package vault
 import (
 	"crypto/tls"
 	"encoding/json"
-	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"net/http"
-	"path"
-	"time"
-
 	bank_vaults "github.com/banzaicloud/bank-vaults/pkg/sdk/vault"
 	vaultapi "github.com/hashicorp/vault/api"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/connection"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
 	odahuflow_errors "github.com/odahu/odahu-flow/packages/operator/pkg/errors"
 	conn_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
+	"net/http"
+	"path"
 )
 
 const (
@@ -156,7 +153,6 @@ func (vcr *vaultConnRepository) UpdateConnection(conn *connection.Connection) er
 	case err == nil:
 		// If err is not nil, then the connection already exists.
 		existedConn.Spec = conn.Spec
-		existedConn.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
 		err = vcr.createOrUpdateConnection(existedConn)
 		if err != nil {
 			conn.Status = existedConn.Status
@@ -169,7 +165,7 @@ func (vcr *vaultConnRepository) UpdateConnection(conn *connection.Connection) er
 	}
 }
 
-func (vcr *vaultConnRepository) CreateConnection(conn *connection.Connection) error {
+func (vcr *vaultConnRepository) SaveConnection(conn *connection.Connection) error {
 	_, err := vcr.GetConnection(conn.ID)
 
 	switch {
@@ -177,8 +173,6 @@ func (vcr *vaultConnRepository) CreateConnection(conn *connection.Connection) er
 		// If err is nil, then the connection already exists.
 		return odahuflow_errors.AlreadyExistError{Entity: conn.ID}
 	case odahuflow_errors.IsNotFoundError(err):
-		conn.Status.CreatedAt = &metav1.Time{Time: time.Now()}
-		conn.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
 		return vcr.createOrUpdateConnection(conn)
 	default:
 		return err
diff --git a/packages/operator/pkg/repository/deployment/mocks/Repository.go b/packages/operator/pkg/repository/deployment/mocks/Repository.go
index d9abd98e9..43d04f0e1 100644
--- a/packages/operator/pkg/repository/deployment/mocks/Repository.go
+++ b/packages/operator/pkg/repository/deployment/mocks/Repository.go
@@ -45,7 +45,7 @@ func (_m *Repository) BeginTransaction(ctx context.Context) (*sql.Tx, error) {
 }
 
 // CreateModelDeployment provides a mock function with given fields: ctx, tx, md
-func (_m *Repository) CreateModelDeployment(ctx context.Context, tx *sql.Tx, md *apisdeployment.ModelDeployment) error {
+func (_m *Repository) SaveModelDeployment(ctx context.Context, tx *sql.Tx, md *apisdeployment.ModelDeployment) error {
 	ret := _m.Called(ctx, tx, md)
 
 	var r0 error
diff --git a/packages/operator/pkg/repository/deployment/postgres/model_deployment.go b/packages/operator/pkg/repository/deployment/postgres/model_deployment.go
index cdbb0d5a4..3ff39b5bc 100644
--- a/packages/operator/pkg/repository/deployment/postgres/model_deployment.go
+++ b/packages/operator/pkg/repository/deployment/postgres/model_deployment.go
@@ -14,13 +14,13 @@ import (
 )
 
 const (
-	ModelDeploymentTable = "odahu_operator_deployment"
-	ModelRouteTable = "odahu_operator_route"
+	ModelDeploymentTable        = "odahu_operator_deployment"
+	ModelRouteTable             = "odahu_operator_route"
 	uniqueViolationPostgresCode = pq.ErrorCode("23505") // unique_violation
 )
 
 var (
-	log = logf.Log.WithName("model-deployment--repository--postgres")
+	log       = logf.Log.WithName("model-deployment--repository--postgres")
 	MaxSize   = 500
 	FirstPage = 0
 	txOptions = &sql.TxOptions{
@@ -251,7 +251,7 @@ func (repo DeploymentRepo) UpdateModelDeploymentStatus(
 	return nil
 }
 
-func (repo DeploymentRepo) CreateModelDeployment(
+func (repo DeploymentRepo) SaveModelDeployment(
 	ctx context.Context, tx *sql.Tx, md *deployment.ModelDeployment) error {
 
 	var qrr utils.Querier
@@ -274,7 +274,7 @@ func (repo DeploymentRepo) CreateModelDeployment(
 	_, err = qrr.ExecContext(
 		ctx,
 		stmt,
-		args...
+		args...,
 	)
 	if err != nil {
 		pqError, ok := err.(*pq.Error)
diff --git a/packages/operator/pkg/repository/deployment/repository.go b/packages/operator/pkg/repository/deployment/repository.go
index 522dd1a99..a911b4475 100644
--- a/packages/operator/pkg/repository/deployment/repository.go
+++ b/packages/operator/pkg/repository/deployment/repository.go
@@ -35,7 +35,7 @@ type Repository interface {
 	DeleteModelDeployment(ctx context.Context, tx *sql.Tx, id string) error
 	UpdateModelDeployment(ctx context.Context, tx *sql.Tx, md *deployment.ModelDeployment) error
 	UpdateModelDeploymentStatus(ctx context.Context, tx *sql.Tx, id string, s v1alpha1.ModelDeploymentStatus) error
-	CreateModelDeployment(ctx context.Context, tx *sql.Tx, md *deployment.ModelDeployment) error
+	SaveModelDeployment(ctx context.Context, tx *sql.Tx, md *deployment.ModelDeployment) error
 	SetDeletionMark(ctx context.Context, tx *sql.Tx, id string, value bool) error
 	BeginTransaction(ctx context.Context) (*sql.Tx, error)
 }
diff --git a/packages/operator/pkg/repository/packaging/mocks/PackagingIntegrationRepository.go b/packages/operator/pkg/repository/packaging/mocks/PackagingIntegrationRepository.go
index e316781e5..c1cfcd331 100644
--- a/packages/operator/pkg/repository/packaging/mocks/PackagingIntegrationRepository.go
+++ b/packages/operator/pkg/repository/packaging/mocks/PackagingIntegrationRepository.go
@@ -15,7 +15,7 @@ type PackagingIntegrationRepository struct {
 }
 
 // CreatePackagingIntegration provides a mock function with given fields: md
-func (_m *PackagingIntegrationRepository) CreatePackagingIntegration(md *packaging.PackagingIntegration) error {
+func (_m *PackagingIntegrationRepository) SavePackagingIntegration(md *packaging.PackagingIntegration) error {
 	ret := _m.Called(md)
 
 	var r0 error
diff --git a/packages/operator/pkg/repository/packaging/mocks/Repository.go b/packages/operator/pkg/repository/packaging/mocks/Repository.go
index bfbdc1d85..81a95d8c4 100644
--- a/packages/operator/pkg/repository/packaging/mocks/Repository.go
+++ b/packages/operator/pkg/repository/packaging/mocks/Repository.go
@@ -45,7 +45,7 @@ func (_m *Repository) BeginTransaction(ctx context.Context) (*sql.Tx, error) {
 }
 
 // CreateModelPackaging provides a mock function with given fields: ctx, tx, mp
-func (_m *Repository) CreateModelPackaging(ctx context.Context, tx *sql.Tx, mp *apispackaging.ModelPackaging) error {
+func (_m *Repository) SaveModelPackaging(ctx context.Context, tx *sql.Tx, mp *apispackaging.ModelPackaging) error {
 	ret := _m.Called(ctx, tx, mp)
 
 	var r0 error
diff --git a/packages/operator/pkg/repository/packaging/postgres/model_packaging.go b/packages/operator/pkg/repository/packaging/postgres/model_packaging.go
index de8b853ea..358a5b731 100644
--- a/packages/operator/pkg/repository/packaging/postgres/model_packaging.go
+++ b/packages/operator/pkg/repository/packaging/postgres/model_packaging.go
@@ -18,7 +18,7 @@ const (
 )
 
 var (
-	log = logf.Log.WithName("model-packaging--repository--postgres")
+	log       = logf.Log.WithName("model-packaging--repository--postgres")
 	txOptions = &sql.TxOptions{
 		Isolation: sql.LevelRepeatableRead,
 		ReadOnly:  false,
@@ -243,7 +243,7 @@ func (repo PackagingRepo) UpdateModelPackagingStatus(
 	return nil
 }
 
-func (repo PackagingRepo) CreateModelPackaging(ctx context.Context, tx *sql.Tx, mp *packaging.ModelPackaging) error {
+func (repo PackagingRepo) SaveModelPackaging(ctx context.Context, tx *sql.Tx, mp *packaging.ModelPackaging) error {
 
 	var qrr utils.Querier
 	qrr = repo.DB
@@ -265,7 +265,7 @@ func (repo PackagingRepo) CreateModelPackaging(ctx context.Context, tx *sql.Tx,
 	_, err = qrr.ExecContext(
 		ctx,
 		stmt,
-		args...
+		args...,
 	)
 	if err != nil {
 		pqError, ok := err.(*pq.Error)
@@ -281,4 +281,3 @@ func (repo PackagingRepo) CreateModelPackaging(ctx context.Context, tx *sql.Tx,
 func (repo PackagingRepo) BeginTransaction(ctx context.Context) (*sql.Tx, error) {
 	return repo.DB.BeginTx(ctx, txOptions)
 }
-
diff --git a/packages/operator/pkg/repository/packaging/postgres/packaging_integration.go b/packages/operator/pkg/repository/packaging/postgres/packaging_integration.go
index 3bfcdbebe..cef5f5b43 100644
--- a/packages/operator/pkg/repository/packaging/postgres/packaging_integration.go
+++ b/packages/operator/pkg/repository/packaging/postgres/packaging_integration.go
@@ -19,13 +19,10 @@ package postgres
 import (
 	"database/sql"
 	"fmt"
-	"time"
-
 	"github.com/lib/pq"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/packaging"
 	odahuErrors "github.com/odahu/odahu-flow/packages/operator/pkg/errors"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 const (
@@ -42,7 +39,7 @@ type PackagingIntegrationRepository struct {
 	DB *sql.DB
 }
 
-func (pir PackagingIntegrationRepository) GetPackagingIntegration(name string) (
+func (pir *PackagingIntegrationRepository) GetPackagingIntegration(name string) (
 	*packaging.PackagingIntegration, error,
 ) {
 
@@ -64,7 +61,7 @@ func (pir PackagingIntegrationRepository) GetPackagingIntegration(name string) (
 
 }
 
-func (pir PackagingIntegrationRepository) GetPackagingIntegrationList(options ...filter.ListOption) (
+func (pir *PackagingIntegrationRepository) GetPackagingIntegrationList(options ...filter.ListOption) (
 	[]packaging.PackagingIntegration, error,
 ) {
 
@@ -106,7 +103,7 @@ func (pir PackagingIntegrationRepository) GetPackagingIntegrationList(options ..
 
 }
 
-func (pir PackagingIntegrationRepository) DeletePackagingIntegration(name string) error {
+func (pir *PackagingIntegrationRepository) DeletePackagingIntegration(name string) error {
 
 	// First try to check that row exists otherwise raise exception to fit interface
 	_, err := pir.GetPackagingIntegration(name)
@@ -124,7 +121,7 @@ func (pir PackagingIntegrationRepository) DeletePackagingIntegration(name string
 	return nil
 }
 
-func (pir PackagingIntegrationRepository) UpdatePackagingIntegration(pi *packaging.PackagingIntegration) error {
+func (pir *PackagingIntegrationRepository) UpdatePackagingIntegration(pi *packaging.PackagingIntegration) error {
 
 	// First try to check that row exists otherwise raise exception to fit interface
 	oldPi, err := pir.GetPackagingIntegration(pi.ID)
@@ -133,7 +130,6 @@ func (pir PackagingIntegrationRepository) UpdatePackagingIntegration(pi *packagi
 	}
 
 	pi.Status = oldPi.Status
-	pi.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
 
 	sqlStatement := fmt.Sprintf("UPDATE %s SET spec = $1, status = $2 WHERE id = $3", packagingIntegrationTable)
 	_, err = pir.DB.Exec(sqlStatement, pi.Spec, pi.Status, pi.ID)
@@ -143,10 +139,7 @@ func (pir PackagingIntegrationRepository) UpdatePackagingIntegration(pi *packagi
 	return nil
 }
 
-func (pir PackagingIntegrationRepository) CreatePackagingIntegration(pi *packaging.PackagingIntegration) error {
-
-	pi.Status.CreatedAt = &metav1.Time{Time: time.Now()}
-	pi.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
+func (pir *PackagingIntegrationRepository) SavePackagingIntegration(pi *packaging.PackagingIntegration) error {
 
 	_, err := pir.DB.Exec(
 		fmt.Sprintf("INSERT INTO %s (id, spec, status) VALUES($1, $2, $3)", packagingIntegrationTable),
diff --git a/packages/operator/pkg/repository/packaging/repository.go b/packages/operator/pkg/repository/packaging/repository.go
index c9a82d566..de966e0ff 100644
--- a/packages/operator/pkg/repository/packaging/repository.go
+++ b/packages/operator/pkg/repository/packaging/repository.go
@@ -36,7 +36,7 @@ type Repository interface {
 	SetDeletionMark(ctx context.Context, tx *sql.Tx, id string, value bool) error
 	UpdateModelPackaging(ctx context.Context, tx *sql.Tx, mp *packaging.ModelPackaging) error
 	UpdateModelPackagingStatus(ctx context.Context, tx *sql.Tx, id string, s v1alpha1.ModelPackagingStatus) error
-	CreateModelPackaging(ctx context.Context, tx *sql.Tx, mp *packaging.ModelPackaging) error
+	SaveModelPackaging(ctx context.Context, tx *sql.Tx, mp *packaging.ModelPackaging) error
 	BeginTransaction(ctx context.Context) (*sql.Tx, error)
 }
 
@@ -45,7 +45,7 @@ type PackagingIntegrationRepository interface {
 	GetPackagingIntegrationList(options ...filter.ListOption) ([]packaging.PackagingIntegration, error)
 	DeletePackagingIntegration(id string) error
 	UpdatePackagingIntegration(md *packaging.PackagingIntegration) error
-	CreatePackagingIntegration(md *packaging.PackagingIntegration) error
+	SavePackagingIntegration(md *packaging.PackagingIntegration) error
 }
 
 type MPFilter struct {
diff --git a/packages/operator/pkg/repository/route/mocks/Repository.go b/packages/operator/pkg/repository/route/mocks/Repository.go
index 91b236092..ba7e64768 100644
--- a/packages/operator/pkg/repository/route/mocks/Repository.go
+++ b/packages/operator/pkg/repository/route/mocks/Repository.go
@@ -44,7 +44,7 @@ func (_m *Repository) BeginTransaction(ctx context.Context) (*sql.Tx, error) {
 }
 
 // CreateModelRoute provides a mock function with given fields: ctx, tx, r
-func (_m *Repository) CreateModelRoute(ctx context.Context, tx *sql.Tx, r *deployment.ModelRoute) error {
+func (_m *Repository) SaveModelRoute(ctx context.Context, tx *sql.Tx, r *deployment.ModelRoute) error {
 	ret := _m.Called(ctx, tx, r)
 
 	var r0 error
diff --git a/packages/operator/pkg/repository/route/postgres/model_route.go b/packages/operator/pkg/repository/route/postgres/model_route.go
index f98b3239d..944687f64 100644
--- a/packages/operator/pkg/repository/route/postgres/model_route.go
+++ b/packages/operator/pkg/repository/route/postgres/model_route.go
@@ -14,21 +14,21 @@ import (
 )
 
 const (
-	ModelRouteTable = "odahu_operator_route"
+	ModelRouteTable             = "odahu_operator_route"
 	uniqueViolationPostgresCode = pq.ErrorCode("23505") // unique_violation
 
-	ClID = "id"
-	ClSpec = "spec"
-	ClStatus = "status"
-	ClDelMark = "deletionmark"
-	ClCreated = "created"
-	ClUpdated = "updated"
-	ClIsDefault = "is_default"
+	ClID          = "id"
+	ClSpec        = "spec"
+	ClStatus      = "status"
+	ClDelMark     = "deletionmark"
+	ClCreated     = "created"
+	ClUpdated     = "updated"
+	ClIsDefault   = "is_default"
 	ClFirstMDName = "spec->'modelDeployments'->0->>'mdName'"
 )
 
 var (
-	log = logf.Log.WithName("model-route--repository--postgres")
+	log       = logf.Log.WithName("model-route--repository--postgres")
 	MaxSize   = 500
 	FirstPage = 0
 	txOptions = &sql.TxOptions{
@@ -260,7 +260,7 @@ func (repo RouteRepo) UpdateModelRouteStatus(
 	return nil
 }
 
-func (repo RouteRepo) CreateModelRoute(
+func (repo RouteRepo) SaveModelRoute(
 	ctx context.Context, tx *sql.Tx, md *route.ModelRoute) error {
 
 	var qrr utils.Querier
@@ -283,7 +283,7 @@ func (repo RouteRepo) CreateModelRoute(
 	_, err = qrr.ExecContext(
 		ctx,
 		stmt,
-		args...
+		args...,
 	)
 	if err != nil {
 		pqError, ok := err.(*pq.Error)
@@ -296,7 +296,6 @@ func (repo RouteRepo) CreateModelRoute(
 
 }
 
-
 func (repo RouteRepo) BeginTransaction(ctx context.Context) (*sql.Tx, error) {
 	return repo.DB.BeginTx(ctx, txOptions)
 }
@@ -364,7 +363,7 @@ func (repo RouteRepo) IsDefault(ctx context.Context, id string, tx *sql.Tx) (boo
 		if err := rows.Scan(&isDefault); err != nil {
 			return false, err
 		}
-		return isDefault, nil  //nolint
+		return isDefault, nil //nolint
 	}
 
 	return false, odahuErrors.NotFoundError{Entity: id}
diff --git a/packages/operator/pkg/repository/route/repository.go b/packages/operator/pkg/repository/route/repository.go
index b4cae6f76..de55bf69c 100644
--- a/packages/operator/pkg/repository/route/repository.go
+++ b/packages/operator/pkg/repository/route/repository.go
@@ -29,7 +29,7 @@ type Repository interface {
 	GetModelRouteList(ctx context.Context, tx *sql.Tx, options ...filter.ListOption) ([]deployment.ModelRoute, error)
 	DeleteModelRoute(ctx context.Context, tx *sql.Tx, name string) error
 	UpdateModelRoute(ctx context.Context, tx *sql.Tx, md *deployment.ModelRoute) error
-	CreateModelRoute(ctx context.Context, tx *sql.Tx, r *deployment.ModelRoute) error
+	SaveModelRoute(ctx context.Context, tx *sql.Tx, r *deployment.ModelRoute) error
 	UpdateModelRouteStatus(ctx context.Context, tx *sql.Tx, id string, s v1alpha1.ModelRouteStatus) error
 	SetDeletionMark(ctx context.Context, tx *sql.Tx, id string, value bool) error
 	BeginTransaction(ctx context.Context) (*sql.Tx, error)
@@ -38,8 +38,7 @@ type Repository interface {
 	IsDefault(ctx context.Context, id string, tx *sql.Tx) (bool, error)
 }
 
-
 type Filter struct {
-	Default	     []bool `name:"is_default" postgres:"is_default"`
-	MdID	     []string `name:"mdId" postgres:"spec->'modelDeployments'->0->>'mdName'"`
-}
\ No newline at end of file
+	Default []bool   `name:"is_default" postgres:"is_default"`
+	MdID    []string `name:"mdId" postgres:"spec->'modelDeployments'->0->>'mdName'"`
+}
diff --git a/packages/operator/pkg/repository/training/mocks/Repository.go b/packages/operator/pkg/repository/training/mocks/Repository.go
index 076999b5b..7cedeeb5a 100644
--- a/packages/operator/pkg/repository/training/mocks/Repository.go
+++ b/packages/operator/pkg/repository/training/mocks/Repository.go
@@ -45,7 +45,7 @@ func (_m *Repository) BeginTransaction(ctx context.Context) (*sql.Tx, error) {
 }
 
 // CreateModelTraining provides a mock function with given fields: ctx, tx, mt
-func (_m *Repository) CreateModelTraining(ctx context.Context, tx *sql.Tx, mt *apistraining.ModelTraining) error {
+func (_m *Repository) SaveModelTraining(ctx context.Context, tx *sql.Tx, mt *apistraining.ModelTraining) error {
 	ret := _m.Called(ctx, tx, mt)
 
 	var r0 error
diff --git a/packages/operator/pkg/repository/training/mocks/ToolchainRepository.go b/packages/operator/pkg/repository/training/mocks/ToolchainRepository.go
index 8e8bb453f..edc83add1 100644
--- a/packages/operator/pkg/repository/training/mocks/ToolchainRepository.go
+++ b/packages/operator/pkg/repository/training/mocks/ToolchainRepository.go
@@ -15,7 +15,7 @@ type ToolchainRepository struct {
 }
 
 // CreateToolchainIntegration provides a mock function with given fields: md
-func (_m *ToolchainRepository) CreateToolchainIntegration(md *training.ToolchainIntegration) error {
+func (_m *ToolchainRepository) SaveToolchainIntegration(md *training.ToolchainIntegration) error {
 	ret := _m.Called(md)
 
 	var r0 error
diff --git a/packages/operator/pkg/repository/training/postgres/model_training.go b/packages/operator/pkg/repository/training/postgres/model_training.go
index 387c67623..732dc0ca6 100644
--- a/packages/operator/pkg/repository/training/postgres/model_training.go
+++ b/packages/operator/pkg/repository/training/postgres/model_training.go
@@ -18,7 +18,7 @@ const (
 )
 
 var (
-	log = logf.Log.WithName("model-training--repository--postgres")
+	log       = logf.Log.WithName("model-training--repository--postgres")
 	txOptions = &sql.TxOptions{
 		Isolation: sql.LevelRepeatableRead,
 		ReadOnly:  false,
@@ -69,7 +69,7 @@ func (repo TrainingRepo) GetModelTraining(ctx context.Context, tx *sql.Tx, id st
 }
 
 func (repo TrainingRepo) GetModelTrainingList(
-	ctx context.Context, tx *sql.Tx, options ...filter.ListOption, ) ([]training.ModelTraining, error) {
+	ctx context.Context, tx *sql.Tx, options ...filter.ListOption) ([]training.ModelTraining, error) {
 
 	var qrr utils.Querier
 	qrr = repo.DB
@@ -246,7 +246,7 @@ func (repo TrainingRepo) UpdateModelTrainingStatus(
 	return nil
 }
 
-func (repo TrainingRepo) CreateModelTraining(ctx context.Context, tx *sql.Tx, mt *training.ModelTraining) error {
+func (repo TrainingRepo) SaveModelTraining(ctx context.Context, tx *sql.Tx, mt *training.ModelTraining) error {
 
 	var qrr utils.Querier
 	qrr = repo.DB
@@ -268,7 +268,7 @@ func (repo TrainingRepo) CreateModelTraining(ctx context.Context, tx *sql.Tx, mt
 	_, err = qrr.ExecContext(
 		ctx,
 		stmt,
-		args...
+		args...,
 	)
 	if err != nil {
 		pqError, ok := err.(*pq.Error)
@@ -281,7 +281,6 @@ func (repo TrainingRepo) CreateModelTraining(ctx context.Context, tx *sql.Tx, mt
 
 }
 
-
 func (repo TrainingRepo) BeginTransaction(ctx context.Context) (*sql.Tx, error) {
 	return repo.DB.BeginTx(ctx, txOptions)
 }
diff --git a/packages/operator/pkg/repository/training/postgres/toolchain_integration.go b/packages/operator/pkg/repository/training/postgres/toolchain_integration.go
index 02fc1ab6a..2f21f7c69 100644
--- a/packages/operator/pkg/repository/training/postgres/toolchain_integration.go
+++ b/packages/operator/pkg/repository/training/postgres/toolchain_integration.go
@@ -7,8 +7,6 @@ import (
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/training"
 	odahuErrors "github.com/odahu/odahu-flow/packages/operator/pkg/errors"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"time"
 )
 
 const (
@@ -113,7 +111,6 @@ func (tr ToolchainRepo) UpdateToolchainIntegration(md *training.ToolchainIntegra
 	}
 
 	md.Status = oldTi.Status
-	md.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
 
 	sqlStatement := fmt.Sprintf("UPDATE %s SET spec = $1, status = $2 WHERE id = $3", toolchainIntegrationTable)
 	_, err = tr.DB.Exec(sqlStatement, md.Spec, md.Status, md.ID)
@@ -123,10 +120,7 @@ func (tr ToolchainRepo) UpdateToolchainIntegration(md *training.ToolchainIntegra
 	return nil
 }
 
-func (tr ToolchainRepo) CreateToolchainIntegration(md *training.ToolchainIntegration) error {
-
-	md.Status.CreatedAt = &metav1.Time{Time: time.Now()}
-	md.Status.UpdatedAt = &metav1.Time{Time: time.Now()}
+func (tr ToolchainRepo) SaveToolchainIntegration(md *training.ToolchainIntegration) error {
 
 	_, err := tr.DB.Exec(
 		fmt.Sprintf("INSERT INTO %s (id, spec, status) VALUES($1, $2, $3)", toolchainIntegrationTable),
diff --git a/packages/operator/pkg/repository/training/repository.go b/packages/operator/pkg/repository/training/repository.go
index 7c7bd943c..b0453c216 100644
--- a/packages/operator/pkg/repository/training/repository.go
+++ b/packages/operator/pkg/repository/training/repository.go
@@ -31,12 +31,12 @@ const (
 type Repository interface {
 	GetModelTraining(ctx context.Context, tx *sql.Tx, id string) (*training.ModelTraining, error)
 	GetModelTrainingList(
-		ctx context.Context, tx *sql.Tx, options ...filter.ListOption, ) ([]training.ModelTraining, error)
+		ctx context.Context, tx *sql.Tx, options ...filter.ListOption) ([]training.ModelTraining, error)
 	DeleteModelTraining(ctx context.Context, tx *sql.Tx, id string) error
 	SetDeletionMark(ctx context.Context, tx *sql.Tx, id string, value bool) error
 	UpdateModelTraining(ctx context.Context, tx *sql.Tx, mt *training.ModelTraining) error
 	UpdateModelTrainingStatus(ctx context.Context, tx *sql.Tx, id string, s v1alpha1.ModelTrainingStatus) error
-	CreateModelTraining(ctx context.Context, tx *sql.Tx, mt *training.ModelTraining) error
+	SaveModelTraining(ctx context.Context, tx *sql.Tx, mt *training.ModelTraining) error
 	BeginTransaction(ctx context.Context) (*sql.Tx, error)
 }
 
@@ -45,7 +45,7 @@ type ToolchainRepository interface {
 	GetToolchainIntegrationList(options ...filter.ListOption) ([]training.ToolchainIntegration, error)
 	DeleteToolchainIntegration(name string) error
 	UpdateToolchainIntegration(md *training.ToolchainIntegration) error
-	CreateToolchainIntegration(md *training.ToolchainIntegration) error
+	SaveToolchainIntegration(md *training.ToolchainIntegration) error
 }
 
 type MTFilter struct {
diff --git a/packages/operator/pkg/service/connection/connection_service.go b/packages/operator/pkg/service/connection/connection_service.go
index 92528c8f5..6fd2db417 100644
--- a/packages/operator/pkg/service/connection/connection_service.go
+++ b/packages/operator/pkg/service/connection/connection_service.go
@@ -22,6 +22,7 @@ import (
 	"github.com/odahu/odahu-flow/packages/operator/pkg/errors"
 	conn_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
 	"go.uber.org/multierr"
+	"time"
 )
 
 // A layer on top of connection repository that prepares data, e.g. base64 decoding
@@ -74,6 +75,8 @@ func (s *serviceImpl) DeleteConnection(id string) error {
 }
 
 func (s *serviceImpl) UpdateConnection(connection connection.Connection) (*connection.Connection, error) {
+	connection.UpdatedAt = time.Now()
+
 	if err := connection.DecodeBase64Fields(); err != nil {
 		return nil, errors.InvalidEntityError{
 			Entity:           fmt.Sprintf("Connection %s", connection.ID),
@@ -92,6 +95,9 @@ func (s *serviceImpl) UpdateConnection(connection connection.Connection) (*conne
 }
 
 func (s *serviceImpl) CreateConnection(connection connection.Connection) (*connection.Connection, error) {
+	connection.CreatedAt = time.Now()
+	connection.UpdatedAt = connection.CreatedAt
+
 	if err := connection.DecodeBase64Fields(); err != nil {
 		return nil, errors.InvalidEntityError{
 			Entity:           fmt.Sprintf("Connection %s", connection.ID),
@@ -99,7 +105,7 @@ func (s *serviceImpl) CreateConnection(connection connection.Connection) (*conne
 		}
 	}
 
-	err := s.repo.CreateConnection(&connection)
+	err := s.repo.SaveConnection(&connection)
 	if err != nil {
 		return nil, err
 	}
diff --git a/packages/operator/pkg/service/connection/mock_connection_repo.go b/packages/operator/pkg/service/connection/mock_connection_repo.go
index 6c2f246a7..f976e1eb4 100644
--- a/packages/operator/pkg/service/connection/mock_connection_repo.go
+++ b/packages/operator/pkg/service/connection/mock_connection_repo.go
@@ -54,7 +54,7 @@ func (c *RepositoryMock) UpdateConnection(conn *connection.Connection) error {
 	return mockedResult.Error(0)
 }
 
-func (c *RepositoryMock) CreateConnection(conn *connection.Connection) error {
+func (c *RepositoryMock) SaveConnection(conn *connection.Connection) error {
 	// Remember the state on passed connection for assertions
 	c.CreatedConnection = *conn
 	mockedResult := c.Called(conn)
diff --git a/packages/operator/pkg/service/deployment/deployment_service.go b/packages/operator/pkg/service/deployment/deployment_service.go
index b5906388c..27f454c4d 100644
--- a/packages/operator/pkg/service/deployment/deployment_service.go
+++ b/packages/operator/pkg/service/deployment/deployment_service.go
@@ -259,10 +259,10 @@ func (s serviceImpl) CreateModelDeployment(ctx context.Context, md *deployment.M
 	defer func() { db_utils.FinishTx(tx, err, log) }()
 
 	md.CreatedAt = time.Now()
-	md.UpdatedAt = time.Now()
+	md.UpdatedAt = md.CreatedAt
 	md.DeletionMark = false
 	md.Status = v1alpha1.ModelDeploymentStatus{}
-	err = s.repo.CreateModelDeployment(ctx, tx, md)
+	err = s.repo.SaveModelDeployment(ctx, tx, md)
 	if err != nil {
 		return
 	}
@@ -273,7 +273,7 @@ func (s serviceImpl) CreateModelDeployment(ctx context.Context, md *deployment.M
 	}
 	// Every Model deployment must have a default HTTP route that sends 100% of traffic to the model
 	defRoute := constructDefaultRoute(md.ID)
-	err = s.mrRepo.CreateModelRoute(ctx, tx, &defRoute)
+	err = s.mrRepo.SaveModelRoute(ctx, tx, &defRoute)
 	if err != nil {
 		return fmt.Errorf("unable to create default ModelRoute: %v", err)
 	}
diff --git a/packages/operator/pkg/service/packaging/mock.go b/packages/operator/pkg/service/packaging/mock.go
new file mode 100644
index 000000000..433a5c100
--- /dev/null
+++ b/packages/operator/pkg/service/packaging/mock.go
@@ -0,0 +1,141 @@
+// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
+
+package packaging
+
+import (
+	context "context"
+
+	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/packaging"
+	filter "github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
+	mock "github.com/stretchr/testify/mock"
+
+	v1alpha1 "github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
+)
+
+// MockService is an autogenerated mock type for the Service type
+type MockService struct {
+	mock.Mock
+}
+
+// CreateModelPackaging provides a mock function with given fields: ctx, mt
+func (_m *MockService) CreateModelPackaging(ctx context.Context, mt *packaging.ModelPackaging) error {
+	ret := _m.Called(ctx, mt)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *packaging.ModelPackaging) error); ok {
+		r0 = rf(ctx, mt)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// DeleteModelPackaging provides a mock function with given fields: ctx, id
+func (_m *MockService) DeleteModelPackaging(ctx context.Context, id string) error {
+	ret := _m.Called(ctx, id)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
+		r0 = rf(ctx, id)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// GetModelPackaging provides a mock function with given fields: ctx, id
+func (_m *MockService) GetModelPackaging(ctx context.Context, id string) (*packaging.ModelPackaging, error) {
+	ret := _m.Called(ctx, id)
+
+	var r0 *packaging.ModelPackaging
+	if rf, ok := ret.Get(0).(func(context.Context, string) *packaging.ModelPackaging); ok {
+		r0 = rf(ctx, id)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*packaging.ModelPackaging)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
+		r1 = rf(ctx, id)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetModelPackagingList provides a mock function with given fields: ctx, options
+func (_m *MockService) GetModelPackagingList(ctx context.Context, options ...filter.ListOption) ([]packaging.ModelPackaging, error) {
+	_va := make([]interface{}, len(options))
+	for _i := range options {
+		_va[_i] = options[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 []packaging.ModelPackaging
+	if rf, ok := ret.Get(0).(func(context.Context, ...filter.ListOption) []packaging.ModelPackaging); ok {
+		r0 = rf(ctx, options...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]packaging.ModelPackaging)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, ...filter.ListOption) error); ok {
+		r1 = rf(ctx, options...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// SetDeletionMark provides a mock function with given fields: ctx, id, value
+func (_m *MockService) SetDeletionMark(ctx context.Context, id string, value bool) error {
+	ret := _m.Called(ctx, id, value)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, bool) error); ok {
+		r0 = rf(ctx, id, value)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// UpdateModelPackaging provides a mock function with given fields: ctx, mt
+func (_m *MockService) UpdateModelPackaging(ctx context.Context, mt *packaging.ModelPackaging) error {
+	ret := _m.Called(ctx, mt)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *packaging.ModelPackaging) error); ok {
+		r0 = rf(ctx, mt)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// UpdateModelPackagingStatus provides a mock function with given fields: ctx, id, status, spec
+func (_m *MockService) UpdateModelPackagingStatus(ctx context.Context, id string, status v1alpha1.ModelPackagingStatus, spec packaging.ModelPackagingSpec) error {
+	ret := _m.Called(ctx, id, status, spec)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, v1alpha1.ModelPackagingStatus, packaging.ModelPackagingSpec) error); ok {
+		r0 = rf(ctx, id, status, spec)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
diff --git a/packages/operator/pkg/service/packaging/packaging_service.go b/packages/operator/pkg/service/packaging/packaging_service.go
index 0383909ef..79f00ee21 100644
--- a/packages/operator/pkg/service/packaging/packaging_service.go
+++ b/packages/operator/pkg/service/packaging/packaging_service.go
@@ -21,8 +21,8 @@ import (
 	"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/packaging"
 	odahu_errors "github.com/odahu/odahu-flow/packages/operator/pkg/errors"
-	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
 	repo "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
 	hashutil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/hash"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 	"time"
@@ -131,10 +131,9 @@ func (s serviceImpl) CreateModelPackaging(ctx context.Context, mp *packaging.Mod
 	mp.Status = v1alpha1.ModelPackagingStatus{
 		State: v1alpha1.ModelPackagingUnknown,
 	}
-	return s.repo.CreateModelPackaging(ctx, nil, mp)
+	return s.repo.SaveModelPackaging(ctx, nil, mp)
 }
 
 func NewService(repo repo.Repository) Service {
 	return &serviceImpl{repo: repo}
 }
-
diff --git a/packages/operator/pkg/service/packaging_integration/mock.go b/packages/operator/pkg/service/packaging_integration/mock.go
new file mode 100644
index 000000000..2a56cce54
--- /dev/null
+++ b/packages/operator/pkg/service/packaging_integration/mock.go
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2021 EPAM Systems
+ *
+ * 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.
+ */
+
+// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
+
+package packaging_integration
+
+import (
+	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/packaging"
+	filter "github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// MockService is an autogenerated mock type for the Service type
+type MockService struct {
+	mock.Mock
+}
+
+// CreatePackagingIntegration provides a mock function with given fields: md
+func (_m *MockService) CreatePackagingIntegration(md *packaging.PackagingIntegration) error {
+	ret := _m.Called(md)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*packaging.PackagingIntegration) error); ok {
+		r0 = rf(md)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// DeletePackagingIntegration provides a mock function with given fields: id
+func (_m *MockService) DeletePackagingIntegration(id string) error {
+	ret := _m.Called(id)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(string) error); ok {
+		r0 = rf(id)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// GetPackagingIntegration provides a mock function with given fields: id
+func (_m *MockService) GetPackagingIntegration(id string) (*packaging.PackagingIntegration, error) {
+	ret := _m.Called(id)
+
+	var r0 *packaging.PackagingIntegration
+	if rf, ok := ret.Get(0).(func(string) *packaging.PackagingIntegration); ok {
+		r0 = rf(id)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*packaging.PackagingIntegration)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(string) error); ok {
+		r1 = rf(id)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetPackagingIntegrationList provides a mock function with given fields: options
+func (_m *MockService) GetPackagingIntegrationList(options ...filter.ListOption) ([]packaging.PackagingIntegration, error) {
+	_va := make([]interface{}, len(options))
+	for _i := range options {
+		_va[_i] = options[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 []packaging.PackagingIntegration
+	if rf, ok := ret.Get(0).(func(...filter.ListOption) []packaging.PackagingIntegration); ok {
+		r0 = rf(options...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]packaging.PackagingIntegration)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(...filter.ListOption) error); ok {
+		r1 = rf(options...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdatePackagingIntegration provides a mock function with given fields: md
+func (_m *MockService) UpdatePackagingIntegration(md *packaging.PackagingIntegration) error {
+	ret := _m.Called(md)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*packaging.PackagingIntegration) error); ok {
+		r0 = rf(md)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
diff --git a/packages/operator/pkg/service/packaging_integration/packaging_integration_service.go b/packages/operator/pkg/service/packaging_integration/packaging_integration_service.go
new file mode 100644
index 000000000..e4add9d4e
--- /dev/null
+++ b/packages/operator/pkg/service/packaging_integration/packaging_integration_service.go
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 EPAM Systems
+ *
+ * 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 packaging_integration //nolint
+
+import (
+	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/packaging"
+	packaging_repo "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
+	"time"
+)
+
+func NewService(repo packaging_repo.PackagingIntegrationRepository) *PackagingIntegrationService {
+	return &PackagingIntegrationService{repo: repo}
+}
+
+type PackagingIntegrationService struct {
+	repo packaging_repo.PackagingIntegrationRepository
+}
+
+func (pis *PackagingIntegrationService) GetPackagingIntegration(id string) (*packaging.PackagingIntegration, error) {
+	return pis.repo.GetPackagingIntegration(id)
+}
+
+func (pis *PackagingIntegrationService) GetPackagingIntegrationList(options ...filter.ListOption) (
+	[]packaging.PackagingIntegration, error,
+) {
+	return pis.repo.GetPackagingIntegrationList(options...)
+}
+
+func (pis *PackagingIntegrationService) CreatePackagingIntegration(md *packaging.PackagingIntegration) error {
+	md.CreatedAt = time.Now()
+	md.UpdatedAt = md.CreatedAt
+	return pis.repo.SavePackagingIntegration(md)
+}
+
+func (pis *PackagingIntegrationService) UpdatePackagingIntegration(md *packaging.PackagingIntegration) error {
+	md.UpdatedAt = time.Now()
+	return pis.repo.UpdatePackagingIntegration(md)
+}
+
+func (pis *PackagingIntegrationService) DeletePackagingIntegration(id string) error {
+	return pis.repo.DeletePackagingIntegration(id)
+}
diff --git a/packages/operator/pkg/service/route/route_service.go b/packages/operator/pkg/service/route/route_service.go
index c2519baef..351460f46 100644
--- a/packages/operator/pkg/service/route/route_service.go
+++ b/packages/operator/pkg/service/route/route_service.go
@@ -28,7 +28,6 @@ import (
 	db_utils "github.com/odahu/odahu-flow/packages/operator/pkg/utils/db"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
 	hashutil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/hash"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 	"time"
 )
@@ -55,9 +54,8 @@ type EventPublisher interface {
 
 type serviceImpl struct {
 	// Repository that has "database/sql" underlying storage
-	repo repo.Repository
+	repo     repo.Repository
 	eventPub EventPublisher
-
 }
 
 func (s serviceImpl) GetModelRoute(ctx context.Context, id string) (*route.ModelRoute, error) {
@@ -91,7 +89,7 @@ func (s serviceImpl) DeleteModelRoute(ctx context.Context, id string) (err error
 	}
 
 	e := event.Event{EntityID: id, EventType: event.ModelRouteDeletedEventType,
-		EventGroup: event.ModelRouteEventGroup, Payload:    nil}
+		EventGroup: event.ModelRouteEventGroup, Payload: nil}
 	if err = s.eventPub.PublishEvent(ctx, tx, e); err != nil {
 		return
 	}
@@ -105,7 +103,7 @@ func (s serviceImpl) SetDeletionMark(ctx context.Context, id string, value bool)
 	if err != nil {
 		return err
 	}
-	defer func(){db_utils.FinishTx(tx, err, log)}()
+	defer func() { db_utils.FinishTx(tx, err, log) }()
 
 	e := event.Event{
 		EntityID:   id,
@@ -122,8 +120,7 @@ func (s serviceImpl) SetDeletionMark(ctx context.Context, id string, value bool)
 func (s serviceImpl) UpdateModelRoute(ctx context.Context, md *route.ModelRoute) (err error) {
 	md.UpdatedAt = time.Now()
 	md.DeletionMark = false
-	md.Status = v1alpha1.ModelRouteStatus{
-	}
+	md.Status = v1alpha1.ModelRouteStatus{}
 
 	var tx *sql.Tx
 	tx, err = s.repo.BeginTransaction(ctx)
@@ -212,7 +209,7 @@ func (s serviceImpl) CreateModelRoute(ctx context.Context, md *route.ModelRoute)
 	if err != nil {
 		return err
 	}
-	defer func(){db_utils.FinishTx(tx, err, log)}()
+	defer func() { db_utils.FinishTx(tx, err, log) }()
 
 	md.CreatedAt = time.Now()
 	md.UpdatedAt = time.Now()
@@ -223,14 +220,6 @@ func (s serviceImpl) CreateModelRoute(ctx context.Context, md *route.ModelRoute)
 	md.Status = v1alpha1.ModelRouteStatus{
 		EdgeURL: "",
 		State:   "",
-		Modifiable: v1alpha1.Modifiable{
-			CreatedAt: &metav1.Time{
-				Time: time.Time{},
-			},
-			UpdatedAt: &metav1.Time{
-				Time: time.Time{},
-			},
-		},
 	}
 
 	e := event.Event{
@@ -243,10 +232,9 @@ func (s serviceImpl) CreateModelRoute(ctx context.Context, md *route.ModelRoute)
 		return err
 	}
 
-	return s.repo.CreateModelRoute(ctx, tx, md)
+	return s.repo.SaveModelRoute(ctx, tx, md)
 }
 
 func NewService(repo repo.Repository, eventPub EventPublisher) Service {
 	return &serviceImpl{repo: repo, eventPub: eventPub}
 }
-
diff --git a/packages/operator/pkg/service/toolchain/mock.go b/packages/operator/pkg/service/toolchain/mock.go
new file mode 100644
index 000000000..8db5bef03
--- /dev/null
+++ b/packages/operator/pkg/service/toolchain/mock.go
@@ -0,0 +1,109 @@
+// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
+
+package toolchain
+
+import (
+	filter "github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
+	mock "github.com/stretchr/testify/mock"
+
+	training "github.com/odahu/odahu-flow/packages/operator/pkg/apis/training"
+)
+
+// MockToolchainService is an autogenerated mock type for the Service type
+type MockToolchainService struct {
+	mock.Mock
+}
+
+// CreateToolchainIntegration provides a mock function with given fields: md
+func (_m *MockToolchainService) CreateToolchainIntegration(md *training.ToolchainIntegration) error {
+	ret := _m.Called(md)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*training.ToolchainIntegration) error); ok {
+		r0 = rf(md)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// DeleteToolchainIntegration provides a mock function with given fields: name
+func (_m *MockToolchainService) DeleteToolchainIntegration(name string) error {
+	ret := _m.Called(name)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(string) error); ok {
+		r0 = rf(name)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// GetToolchainIntegration provides a mock function with given fields: name
+func (_m *MockToolchainService) GetToolchainIntegration(name string) (*training.ToolchainIntegration, error) {
+	ret := _m.Called(name)
+
+	var r0 *training.ToolchainIntegration
+	if rf, ok := ret.Get(0).(func(string) *training.ToolchainIntegration); ok {
+		r0 = rf(name)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*training.ToolchainIntegration)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(string) error); ok {
+		r1 = rf(name)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetToolchainIntegrationList provides a mock function with given fields: options
+func (_m *MockToolchainService) GetToolchainIntegrationList(options ...filter.ListOption) ([]training.ToolchainIntegration, error) {
+	_va := make([]interface{}, len(options))
+	for _i := range options {
+		_va[_i] = options[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 []training.ToolchainIntegration
+	if rf, ok := ret.Get(0).(func(...filter.ListOption) []training.ToolchainIntegration); ok {
+		r0 = rf(options...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]training.ToolchainIntegration)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(...filter.ListOption) error); ok {
+		r1 = rf(options...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateToolchainIntegration provides a mock function with given fields: md
+func (_m *MockToolchainService) UpdateToolchainIntegration(md *training.ToolchainIntegration) error {
+	ret := _m.Called(md)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*training.ToolchainIntegration) error); ok {
+		r0 = rf(md)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
diff --git a/packages/operator/pkg/service/toolchain/service.go b/packages/operator/pkg/service/toolchain/service.go
new file mode 100644
index 000000000..c1dc50b76
--- /dev/null
+++ b/packages/operator/pkg/service/toolchain/service.go
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 EPAM Systems
+ *
+ * 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 toolchain
+
+import (
+	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/training"
+	training2 "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
+	"time"
+)
+
+func NewService(repo training2.ToolchainRepository) *Service {
+	return &Service{repo: repo}
+}
+
+type Service struct {
+	repo training2.ToolchainRepository
+}
+
+func (tis *Service) GetToolchainIntegration(id string) (*training.ToolchainIntegration, error) {
+	return tis.repo.GetToolchainIntegration(id)
+}
+
+func (tis *Service) GetToolchainIntegrationList(options ...filter.ListOption) (
+	[]training.ToolchainIntegration, error,
+) {
+	return tis.repo.GetToolchainIntegrationList(options...)
+}
+
+func (tis *Service) CreateToolchainIntegration(md *training.ToolchainIntegration) error {
+	md.CreatedAt = time.Now()
+	md.UpdatedAt = md.CreatedAt
+	return tis.repo.SaveToolchainIntegration(md)
+}
+
+func (tis *Service) UpdateToolchainIntegration(md *training.ToolchainIntegration) error {
+	md.UpdatedAt = time.Now()
+	return tis.repo.UpdateToolchainIntegration(md)
+}
+
+func (tis *Service) DeleteToolchainIntegration(id string) error {
+	return tis.repo.DeleteToolchainIntegration(id)
+}
diff --git a/packages/operator/pkg/service/training/training_service.go b/packages/operator/pkg/service/training/training_service.go
index 74aa3d2f8..f6a7e3756 100644
--- a/packages/operator/pkg/service/training/training_service.go
+++ b/packages/operator/pkg/service/training/training_service.go
@@ -21,8 +21,8 @@ import (
 	"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/training"
 	odahu_errors "github.com/odahu/odahu-flow/packages/operator/pkg/errors"
+	repo "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
-	repo"github.com/odahu/odahu-flow/packages/operator/pkg/repository/training"
 	hashutil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/hash"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 	"time"
@@ -131,10 +131,9 @@ func (s serviceImpl) CreateModelTraining(ctx context.Context, mt *training.Model
 	mt.Status = v1alpha1.ModelTrainingStatus{
 		State: v1alpha1.ModelTrainingUnknown,
 	}
-	return s.repo.CreateModelTraining(ctx, nil, mt)
+	return s.repo.SaveModelTraining(ctx, nil, mt)
 }
 
 func NewService(repo repo.Repository) Service {
 	return &serviceImpl{repo: repo}
 }
-
diff --git a/packages/sdk/odahuflow/sdk/models/connection.py b/packages/sdk/odahuflow/sdk/models/connection.py
index 22323b8b0..393d1f85c 100644
--- a/packages/sdk/odahuflow/sdk/models/connection.py
+++ b/packages/sdk/odahuflow/sdk/models/connection.py
@@ -17,31 +17,41 @@ class Connection(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, id: str=None, spec: ConnectionSpec=None, status: ConnectionStatus=None):  # noqa: E501
+    def __init__(self, created_at: str=None, id: str=None, spec: ConnectionSpec=None, status: ConnectionStatus=None, updated_at: str=None):  # noqa: E501
         """Connection - a model defined in Swagger
 
+        :param created_at: The created_at of this Connection.  # noqa: E501
+        :type created_at: str
         :param id: The id of this Connection.  # noqa: E501
         :type id: str
         :param spec: The spec of this Connection.  # noqa: E501
         :type spec: ConnectionSpec
         :param status: The status of this Connection.  # noqa: E501
         :type status: ConnectionStatus
+        :param updated_at: The updated_at of this Connection.  # noqa: E501
+        :type updated_at: str
         """
         self.swagger_types = {
+            'created_at': str,
             'id': str,
             'spec': ConnectionSpec,
-            'status': ConnectionStatus
+            'status': ConnectionStatus,
+            'updated_at': str
         }
 
         self.attribute_map = {
+            'created_at': 'createdAt',
             'id': 'id',
             'spec': 'spec',
-            'status': 'status'
+            'status': 'status',
+            'updated_at': 'updatedAt'
         }
 
+        self._created_at = created_at
         self._id = id
         self._spec = spec
         self._status = status
+        self._updated_at = updated_at
 
     @classmethod
     def from_dict(cls, dikt) -> 'Connection':
@@ -54,6 +64,29 @@ def from_dict(cls, dikt) -> 'Connection':
         """
         return util.deserialize_model(dikt, cls)
 
+    @property
+    def created_at(self) -> str:
+        """Gets the created_at of this Connection.
+
+        CreatedAt  # noqa: E501
+
+        :return: The created_at of this Connection.
+        :rtype: str
+        """
+        return self._created_at
+
+    @created_at.setter
+    def created_at(self, created_at: str):
+        """Sets the created_at of this Connection.
+
+        CreatedAt  # noqa: E501
+
+        :param created_at: The created_at of this Connection.
+        :type created_at: str
+        """
+
+        self._created_at = created_at
+
     @property
     def id(self) -> str:
         """Gets the id of this Connection.
@@ -122,3 +155,26 @@ def status(self, status: ConnectionStatus):
         """
 
         self._status = status
+
+    @property
+    def updated_at(self) -> str:
+        """Gets the updated_at of this Connection.
+
+        UpdatedAt  # noqa: E501
+
+        :return: The updated_at of this Connection.
+        :rtype: str
+        """
+        return self._updated_at
+
+    @updated_at.setter
+    def updated_at(self, updated_at: str):
+        """Sets the updated_at of this Connection.
+
+        UpdatedAt  # noqa: E501
+
+        :param updated_at: The updated_at of this Connection.
+        :type updated_at: str
+        """
+
+        self._updated_at = updated_at
diff --git a/packages/sdk/odahuflow/sdk/models/connection_status.py b/packages/sdk/odahuflow/sdk/models/connection_status.py
index 4544d6a09..b5cdcf38e 100644
--- a/packages/sdk/odahuflow/sdk/models/connection_status.py
+++ b/packages/sdk/odahuflow/sdk/models/connection_status.py
@@ -15,36 +15,26 @@ class ConnectionStatus(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, created_at: str=None, secret_name: str=None, service_account: str=None, updated_at: str=None):  # noqa: E501
+    def __init__(self, secret_name: str=None, service_account: str=None):  # noqa: E501
         """ConnectionStatus - a model defined in Swagger
 
-        :param created_at: The created_at of this ConnectionStatus.  # noqa: E501
-        :type created_at: str
         :param secret_name: The secret_name of this ConnectionStatus.  # noqa: E501
         :type secret_name: str
         :param service_account: The service_account of this ConnectionStatus.  # noqa: E501
         :type service_account: str
-        :param updated_at: The updated_at of this ConnectionStatus.  # noqa: E501
-        :type updated_at: str
         """
         self.swagger_types = {
-            'created_at': str,
             'secret_name': str,
-            'service_account': str,
-            'updated_at': str
+            'service_account': str
         }
 
         self.attribute_map = {
-            'created_at': 'createdAt',
             'secret_name': 'secretName',
-            'service_account': 'serviceAccount',
-            'updated_at': 'updatedAt'
+            'service_account': 'serviceAccount'
         }
 
-        self._created_at = created_at
         self._secret_name = secret_name
         self._service_account = service_account
-        self._updated_at = updated_at
 
     @classmethod
     def from_dict(cls, dikt) -> 'ConnectionStatus':
@@ -57,27 +47,6 @@ def from_dict(cls, dikt) -> 'ConnectionStatus':
         """
         return util.deserialize_model(dikt, cls)
 
-    @property
-    def created_at(self) -> str:
-        """Gets the created_at of this ConnectionStatus.
-
-
-        :return: The created_at of this ConnectionStatus.
-        :rtype: str
-        """
-        return self._created_at
-
-    @created_at.setter
-    def created_at(self, created_at: str):
-        """Sets the created_at of this ConnectionStatus.
-
-
-        :param created_at: The created_at of this ConnectionStatus.
-        :type created_at: str
-        """
-
-        self._created_at = created_at
-
     @property
     def secret_name(self) -> str:
         """Gets the secret_name of this ConnectionStatus.
@@ -123,24 +92,3 @@ def service_account(self, service_account: str):
         """
 
         self._service_account = service_account
-
-    @property
-    def updated_at(self) -> str:
-        """Gets the updated_at of this ConnectionStatus.
-
-
-        :return: The updated_at of this ConnectionStatus.
-        :rtype: str
-        """
-        return self._updated_at
-
-    @updated_at.setter
-    def updated_at(self, updated_at: str):
-        """Sets the updated_at of this ConnectionStatus.
-
-
-        :param updated_at: The updated_at of this ConnectionStatus.
-        :type updated_at: str
-        """
-
-        self._updated_at = updated_at
diff --git a/packages/sdk/odahuflow/sdk/models/model_deployment_status.py b/packages/sdk/odahuflow/sdk/models/model_deployment_status.py
index 680496b65..8c7b9524a 100644
--- a/packages/sdk/odahuflow/sdk/models/model_deployment_status.py
+++ b/packages/sdk/odahuflow/sdk/models/model_deployment_status.py
@@ -15,13 +15,11 @@ class ModelDeploymentStatus(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, available_replicas: int=None, created_at: str=None, deployment: str=None, host_header: str=None, last_updated_time: str=None, model_name: str=None, model_version: str=None, replicas: int=None, state: str=None, updated_at: str=None):  # noqa: E501
+    def __init__(self, available_replicas: int=None, deployment: str=None, host_header: str=None, last_updated_time: str=None, model_name: str=None, model_version: str=None, replicas: int=None, state: str=None):  # noqa: E501
         """ModelDeploymentStatus - a model defined in Swagger
 
         :param available_replicas: The available_replicas of this ModelDeploymentStatus.  # noqa: E501
         :type available_replicas: int
-        :param created_at: The created_at of this ModelDeploymentStatus.  # noqa: E501
-        :type created_at: str
         :param deployment: The deployment of this ModelDeploymentStatus.  # noqa: E501
         :type deployment: str
         :param host_header: The host_header of this ModelDeploymentStatus.  # noqa: E501
@@ -36,37 +34,30 @@ def __init__(self, available_replicas: int=None, created_at: str=None, deploymen
         :type replicas: int
         :param state: The state of this ModelDeploymentStatus.  # noqa: E501
         :type state: str
-        :param updated_at: The updated_at of this ModelDeploymentStatus.  # noqa: E501
-        :type updated_at: str
         """
         self.swagger_types = {
             'available_replicas': int,
-            'created_at': str,
             'deployment': str,
             'host_header': str,
             'last_updated_time': str,
             'model_name': str,
             'model_version': str,
             'replicas': int,
-            'state': str,
-            'updated_at': str
+            'state': str
         }
 
         self.attribute_map = {
             'available_replicas': 'availableReplicas',
-            'created_at': 'createdAt',
             'deployment': 'deployment',
             'host_header': 'hostHeader',
             'last_updated_time': 'lastUpdatedTime',
             'model_name': 'modelName',
             'model_version': 'modelVersion',
             'replicas': 'replicas',
-            'state': 'state',
-            'updated_at': 'updatedAt'
+            'state': 'state'
         }
 
         self._available_replicas = available_replicas
-        self._created_at = created_at
         self._deployment = deployment
         self._host_header = host_header
         self._last_updated_time = last_updated_time
@@ -74,7 +65,6 @@ def __init__(self, available_replicas: int=None, created_at: str=None, deploymen
         self._model_version = model_version
         self._replicas = replicas
         self._state = state
-        self._updated_at = updated_at
 
     @classmethod
     def from_dict(cls, dikt) -> 'ModelDeploymentStatus':
@@ -110,27 +100,6 @@ def available_replicas(self, available_replicas: int):
 
         self._available_replicas = available_replicas
 
-    @property
-    def created_at(self) -> str:
-        """Gets the created_at of this ModelDeploymentStatus.
-
-
-        :return: The created_at of this ModelDeploymentStatus.
-        :rtype: str
-        """
-        return self._created_at
-
-    @created_at.setter
-    def created_at(self, created_at: str):
-        """Sets the created_at of this ModelDeploymentStatus.
-
-
-        :param created_at: The created_at of this ModelDeploymentStatus.
-        :type created_at: str
-        """
-
-        self._created_at = created_at
-
     @property
     def deployment(self) -> str:
         """Gets the deployment of this ModelDeploymentStatus.
@@ -291,24 +260,3 @@ def state(self, state: str):
         """
 
         self._state = state
-
-    @property
-    def updated_at(self) -> str:
-        """Gets the updated_at of this ModelDeploymentStatus.
-
-
-        :return: The updated_at of this ModelDeploymentStatus.
-        :rtype: str
-        """
-        return self._updated_at
-
-    @updated_at.setter
-    def updated_at(self, updated_at: str):
-        """Sets the updated_at of this ModelDeploymentStatus.
-
-
-        :param updated_at: The updated_at of this ModelDeploymentStatus.
-        :type updated_at: str
-        """
-
-        self._updated_at = updated_at
diff --git a/packages/sdk/odahuflow/sdk/models/model_packaging_status.py b/packages/sdk/odahuflow/sdk/models/model_packaging_status.py
index dc32f3174..5f68f9dda 100644
--- a/packages/sdk/odahuflow/sdk/models/model_packaging_status.py
+++ b/packages/sdk/odahuflow/sdk/models/model_packaging_status.py
@@ -16,11 +16,9 @@ class ModelPackagingStatus(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, created_at: str=None, exit_code: int=None, message: str=None, pod_name: str=None, reason: str=None, results: List[ModelPackagingResult]=None, state: str=None, updated_at: str=None):  # noqa: E501
+    def __init__(self, exit_code: int=None, message: str=None, pod_name: str=None, reason: str=None, results: List[ModelPackagingResult]=None, state: str=None):  # noqa: E501
         """ModelPackagingStatus - a model defined in Swagger
 
-        :param created_at: The created_at of this ModelPackagingStatus.  # noqa: E501
-        :type created_at: str
         :param exit_code: The exit_code of this ModelPackagingStatus.  # noqa: E501
         :type exit_code: int
         :param message: The message of this ModelPackagingStatus.  # noqa: E501
@@ -33,39 +31,31 @@ def __init__(self, created_at: str=None, exit_code: int=None, message: str=None,
         :type results: List[ModelPackagingResult]
         :param state: The state of this ModelPackagingStatus.  # noqa: E501
         :type state: str
-        :param updated_at: The updated_at of this ModelPackagingStatus.  # noqa: E501
-        :type updated_at: str
         """
         self.swagger_types = {
-            'created_at': str,
             'exit_code': int,
             'message': str,
             'pod_name': str,
             'reason': str,
             'results': List[ModelPackagingResult],
-            'state': str,
-            'updated_at': str
+            'state': str
         }
 
         self.attribute_map = {
-            'created_at': 'createdAt',
             'exit_code': 'exitCode',
             'message': 'message',
             'pod_name': 'podName',
             'reason': 'reason',
             'results': 'results',
-            'state': 'state',
-            'updated_at': 'updatedAt'
+            'state': 'state'
         }
 
-        self._created_at = created_at
         self._exit_code = exit_code
         self._message = message
         self._pod_name = pod_name
         self._reason = reason
         self._results = results
         self._state = state
-        self._updated_at = updated_at
 
     @classmethod
     def from_dict(cls, dikt) -> 'ModelPackagingStatus':
@@ -78,27 +68,6 @@ def from_dict(cls, dikt) -> 'ModelPackagingStatus':
         """
         return util.deserialize_model(dikt, cls)
 
-    @property
-    def created_at(self) -> str:
-        """Gets the created_at of this ModelPackagingStatus.
-
-
-        :return: The created_at of this ModelPackagingStatus.
-        :rtype: str
-        """
-        return self._created_at
-
-    @created_at.setter
-    def created_at(self, created_at: str):
-        """Sets the created_at of this ModelPackagingStatus.
-
-
-        :param created_at: The created_at of this ModelPackagingStatus.
-        :type created_at: str
-        """
-
-        self._created_at = created_at
-
     @property
     def exit_code(self) -> int:
         """Gets the exit_code of this ModelPackagingStatus.
@@ -236,24 +205,3 @@ def state(self, state: str):
         """
 
         self._state = state
-
-    @property
-    def updated_at(self) -> str:
-        """Gets the updated_at of this ModelPackagingStatus.
-
-
-        :return: The updated_at of this ModelPackagingStatus.
-        :rtype: str
-        """
-        return self._updated_at
-
-    @updated_at.setter
-    def updated_at(self, updated_at: str):
-        """Sets the updated_at of this ModelPackagingStatus.
-
-
-        :param updated_at: The updated_at of this ModelPackagingStatus.
-        :type updated_at: str
-        """
-
-        self._updated_at = updated_at
diff --git a/packages/sdk/odahuflow/sdk/models/model_route_status.py b/packages/sdk/odahuflow/sdk/models/model_route_status.py
index 5db1f4235..c6853a144 100644
--- a/packages/sdk/odahuflow/sdk/models/model_route_status.py
+++ b/packages/sdk/odahuflow/sdk/models/model_route_status.py
@@ -15,36 +15,26 @@ class ModelRouteStatus(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, created_at: str=None, edge_url: str=None, state: str=None, updated_at: str=None):  # noqa: E501
+    def __init__(self, edge_url: str=None, state: str=None):  # noqa: E501
         """ModelRouteStatus - a model defined in Swagger
 
-        :param created_at: The created_at of this ModelRouteStatus.  # noqa: E501
-        :type created_at: str
         :param edge_url: The edge_url of this ModelRouteStatus.  # noqa: E501
         :type edge_url: str
         :param state: The state of this ModelRouteStatus.  # noqa: E501
         :type state: str
-        :param updated_at: The updated_at of this ModelRouteStatus.  # noqa: E501
-        :type updated_at: str
         """
         self.swagger_types = {
-            'created_at': str,
             'edge_url': str,
-            'state': str,
-            'updated_at': str
+            'state': str
         }
 
         self.attribute_map = {
-            'created_at': 'createdAt',
             'edge_url': 'edgeUrl',
-            'state': 'state',
-            'updated_at': 'updatedAt'
+            'state': 'state'
         }
 
-        self._created_at = created_at
         self._edge_url = edge_url
         self._state = state
-        self._updated_at = updated_at
 
     @classmethod
     def from_dict(cls, dikt) -> 'ModelRouteStatus':
@@ -57,27 +47,6 @@ def from_dict(cls, dikt) -> 'ModelRouteStatus':
         """
         return util.deserialize_model(dikt, cls)
 
-    @property
-    def created_at(self) -> str:
-        """Gets the created_at of this ModelRouteStatus.
-
-
-        :return: The created_at of this ModelRouteStatus.
-        :rtype: str
-        """
-        return self._created_at
-
-    @created_at.setter
-    def created_at(self, created_at: str):
-        """Sets the created_at of this ModelRouteStatus.
-
-
-        :param created_at: The created_at of this ModelRouteStatus.
-        :type created_at: str
-        """
-
-        self._created_at = created_at
-
     @property
     def edge_url(self) -> str:
         """Gets the edge_url of this ModelRouteStatus.
@@ -123,24 +92,3 @@ def state(self, state: str):
         """
 
         self._state = state
-
-    @property
-    def updated_at(self) -> str:
-        """Gets the updated_at of this ModelRouteStatus.
-
-
-        :return: The updated_at of this ModelRouteStatus.
-        :rtype: str
-        """
-        return self._updated_at
-
-    @updated_at.setter
-    def updated_at(self, updated_at: str):
-        """Sets the updated_at of this ModelRouteStatus.
-
-
-        :param updated_at: The updated_at of this ModelRouteStatus.
-        :type updated_at: str
-        """
-
-        self._updated_at = updated_at
diff --git a/packages/sdk/odahuflow/sdk/models/model_training_status.py b/packages/sdk/odahuflow/sdk/models/model_training_status.py
index 3890b17f5..ffaa19d1a 100644
--- a/packages/sdk/odahuflow/sdk/models/model_training_status.py
+++ b/packages/sdk/odahuflow/sdk/models/model_training_status.py
@@ -16,13 +16,11 @@ class ModelTrainingStatus(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, artifacts: List[TrainingResult]=None, created_at: str=None, exit_code: int=None, message: str=None, pod_name: str=None, reason: str=None, state: str=None, updated_at: str=None):  # noqa: E501
+    def __init__(self, artifacts: List[TrainingResult]=None, exit_code: int=None, message: str=None, pod_name: str=None, reason: str=None, state: str=None):  # noqa: E501
         """ModelTrainingStatus - a model defined in Swagger
 
         :param artifacts: The artifacts of this ModelTrainingStatus.  # noqa: E501
         :type artifacts: List[TrainingResult]
-        :param created_at: The created_at of this ModelTrainingStatus.  # noqa: E501
-        :type created_at: str
         :param exit_code: The exit_code of this ModelTrainingStatus.  # noqa: E501
         :type exit_code: int
         :param message: The message of this ModelTrainingStatus.  # noqa: E501
@@ -33,39 +31,31 @@ def __init__(self, artifacts: List[TrainingResult]=None, created_at: str=None, e
         :type reason: str
         :param state: The state of this ModelTrainingStatus.  # noqa: E501
         :type state: str
-        :param updated_at: The updated_at of this ModelTrainingStatus.  # noqa: E501
-        :type updated_at: str
         """
         self.swagger_types = {
             'artifacts': List[TrainingResult],
-            'created_at': str,
             'exit_code': int,
             'message': str,
             'pod_name': str,
             'reason': str,
-            'state': str,
-            'updated_at': str
+            'state': str
         }
 
         self.attribute_map = {
             'artifacts': 'artifacts',
-            'created_at': 'createdAt',
             'exit_code': 'exitCode',
             'message': 'message',
             'pod_name': 'podName',
             'reason': 'reason',
-            'state': 'state',
-            'updated_at': 'updatedAt'
+            'state': 'state'
         }
 
         self._artifacts = artifacts
-        self._created_at = created_at
         self._exit_code = exit_code
         self._message = message
         self._pod_name = pod_name
         self._reason = reason
         self._state = state
-        self._updated_at = updated_at
 
     @classmethod
     def from_dict(cls, dikt) -> 'ModelTrainingStatus':
@@ -101,27 +91,6 @@ def artifacts(self, artifacts: List[TrainingResult]):
 
         self._artifacts = artifacts
 
-    @property
-    def created_at(self) -> str:
-        """Gets the created_at of this ModelTrainingStatus.
-
-
-        :return: The created_at of this ModelTrainingStatus.
-        :rtype: str
-        """
-        return self._created_at
-
-    @created_at.setter
-    def created_at(self, created_at: str):
-        """Sets the created_at of this ModelTrainingStatus.
-
-
-        :param created_at: The created_at of this ModelTrainingStatus.
-        :type created_at: str
-        """
-
-        self._created_at = created_at
-
     @property
     def exit_code(self) -> int:
         """Gets the exit_code of this ModelTrainingStatus.
@@ -236,24 +205,3 @@ def state(self, state: str):
         """
 
         self._state = state
-
-    @property
-    def updated_at(self) -> str:
-        """Gets the updated_at of this ModelTrainingStatus.
-
-
-        :return: The updated_at of this ModelTrainingStatus.
-        :rtype: str
-        """
-        return self._updated_at
-
-    @updated_at.setter
-    def updated_at(self, updated_at: str):
-        """Sets the updated_at of this ModelTrainingStatus.
-
-
-        :param updated_at: The updated_at of this ModelTrainingStatus.
-        :type updated_at: str
-        """
-
-        self._updated_at = updated_at
diff --git a/packages/sdk/odahuflow/sdk/models/packaging_integration.py b/packages/sdk/odahuflow/sdk/models/packaging_integration.py
index d47a31d47..8b45eb613 100644
--- a/packages/sdk/odahuflow/sdk/models/packaging_integration.py
+++ b/packages/sdk/odahuflow/sdk/models/packaging_integration.py
@@ -17,31 +17,41 @@ class PackagingIntegration(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, id: str=None, spec: PackagingIntegrationSpec=None, status: PackagingIntegrationStatus=None):  # noqa: E501
+    def __init__(self, created_at: str=None, id: str=None, spec: PackagingIntegrationSpec=None, status: PackagingIntegrationStatus=None, updated_at: str=None):  # noqa: E501
         """PackagingIntegration - a model defined in Swagger
 
+        :param created_at: The created_at of this PackagingIntegration.  # noqa: E501
+        :type created_at: str
         :param id: The id of this PackagingIntegration.  # noqa: E501
         :type id: str
         :param spec: The spec of this PackagingIntegration.  # noqa: E501
         :type spec: PackagingIntegrationSpec
         :param status: The status of this PackagingIntegration.  # noqa: E501
         :type status: PackagingIntegrationStatus
+        :param updated_at: The updated_at of this PackagingIntegration.  # noqa: E501
+        :type updated_at: str
         """
         self.swagger_types = {
+            'created_at': str,
             'id': str,
             'spec': PackagingIntegrationSpec,
-            'status': PackagingIntegrationStatus
+            'status': PackagingIntegrationStatus,
+            'updated_at': str
         }
 
         self.attribute_map = {
+            'created_at': 'createdAt',
             'id': 'id',
             'spec': 'spec',
-            'status': 'status'
+            'status': 'status',
+            'updated_at': 'updatedAt'
         }
 
+        self._created_at = created_at
         self._id = id
         self._spec = spec
         self._status = status
+        self._updated_at = updated_at
 
     @classmethod
     def from_dict(cls, dikt) -> 'PackagingIntegration':
@@ -54,6 +64,29 @@ def from_dict(cls, dikt) -> 'PackagingIntegration':
         """
         return util.deserialize_model(dikt, cls)
 
+    @property
+    def created_at(self) -> str:
+        """Gets the created_at of this PackagingIntegration.
+
+        CreatedAt  # noqa: E501
+
+        :return: The created_at of this PackagingIntegration.
+        :rtype: str
+        """
+        return self._created_at
+
+    @created_at.setter
+    def created_at(self, created_at: str):
+        """Sets the created_at of this PackagingIntegration.
+
+        CreatedAt  # noqa: E501
+
+        :param created_at: The created_at of this PackagingIntegration.
+        :type created_at: str
+        """
+
+        self._created_at = created_at
+
     @property
     def id(self) -> str:
         """Gets the id of this PackagingIntegration.
@@ -122,3 +155,26 @@ def status(self, status: PackagingIntegrationStatus):
         """
 
         self._status = status
+
+    @property
+    def updated_at(self) -> str:
+        """Gets the updated_at of this PackagingIntegration.
+
+        UpdatedAt  # noqa: E501
+
+        :return: The updated_at of this PackagingIntegration.
+        :rtype: str
+        """
+        return self._updated_at
+
+    @updated_at.setter
+    def updated_at(self, updated_at: str):
+        """Sets the updated_at of this PackagingIntegration.
+
+        UpdatedAt  # noqa: E501
+
+        :param updated_at: The updated_at of this PackagingIntegration.
+        :type updated_at: str
+        """
+
+        self._updated_at = updated_at
diff --git a/packages/sdk/odahuflow/sdk/models/packaging_integration_status.py b/packages/sdk/odahuflow/sdk/models/packaging_integration_status.py
index 04a15de0c..53a398832 100644
--- a/packages/sdk/odahuflow/sdk/models/packaging_integration_status.py
+++ b/packages/sdk/odahuflow/sdk/models/packaging_integration_status.py
@@ -15,27 +15,16 @@ class PackagingIntegrationStatus(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, created_at: str=None, updated_at: str=None):  # noqa: E501
+    def __init__(self):  # noqa: E501
         """PackagingIntegrationStatus - a model defined in Swagger
 
-        :param created_at: The created_at of this PackagingIntegrationStatus.  # noqa: E501
-        :type created_at: str
-        :param updated_at: The updated_at of this PackagingIntegrationStatus.  # noqa: E501
-        :type updated_at: str
         """
         self.swagger_types = {
-            'created_at': str,
-            'updated_at': str
         }
 
         self.attribute_map = {
-            'created_at': 'createdAt',
-            'updated_at': 'updatedAt'
         }
 
-        self._created_at = created_at
-        self._updated_at = updated_at
-
     @classmethod
     def from_dict(cls, dikt) -> 'PackagingIntegrationStatus':
         """Returns the dict as a model
@@ -46,45 +35,3 @@ def from_dict(cls, dikt) -> 'PackagingIntegrationStatus':
         :rtype: PackagingIntegrationStatus
         """
         return util.deserialize_model(dikt, cls)
-
-    @property
-    def created_at(self) -> str:
-        """Gets the created_at of this PackagingIntegrationStatus.
-
-
-        :return: The created_at of this PackagingIntegrationStatus.
-        :rtype: str
-        """
-        return self._created_at
-
-    @created_at.setter
-    def created_at(self, created_at: str):
-        """Sets the created_at of this PackagingIntegrationStatus.
-
-
-        :param created_at: The created_at of this PackagingIntegrationStatus.
-        :type created_at: str
-        """
-
-        self._created_at = created_at
-
-    @property
-    def updated_at(self) -> str:
-        """Gets the updated_at of this PackagingIntegrationStatus.
-
-
-        :return: The updated_at of this PackagingIntegrationStatus.
-        :rtype: str
-        """
-        return self._updated_at
-
-    @updated_at.setter
-    def updated_at(self, updated_at: str):
-        """Sets the updated_at of this PackagingIntegrationStatus.
-
-
-        :param updated_at: The updated_at of this PackagingIntegrationStatus.
-        :type updated_at: str
-        """
-
-        self._updated_at = updated_at
diff --git a/packages/sdk/odahuflow/sdk/models/toolchain_integration.py b/packages/sdk/odahuflow/sdk/models/toolchain_integration.py
index a453ffa77..3bdeffe5f 100644
--- a/packages/sdk/odahuflow/sdk/models/toolchain_integration.py
+++ b/packages/sdk/odahuflow/sdk/models/toolchain_integration.py
@@ -17,31 +17,41 @@ class ToolchainIntegration(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, id: str=None, spec: ToolchainIntegrationSpec=None, status: ToolchainIntegrationStatus=None):  # noqa: E501
+    def __init__(self, created_at: str=None, id: str=None, spec: ToolchainIntegrationSpec=None, status: ToolchainIntegrationStatus=None, updated_at: str=None):  # noqa: E501
         """ToolchainIntegration - a model defined in Swagger
 
+        :param created_at: The created_at of this ToolchainIntegration.  # noqa: E501
+        :type created_at: str
         :param id: The id of this ToolchainIntegration.  # noqa: E501
         :type id: str
         :param spec: The spec of this ToolchainIntegration.  # noqa: E501
         :type spec: ToolchainIntegrationSpec
         :param status: The status of this ToolchainIntegration.  # noqa: E501
         :type status: ToolchainIntegrationStatus
+        :param updated_at: The updated_at of this ToolchainIntegration.  # noqa: E501
+        :type updated_at: str
         """
         self.swagger_types = {
+            'created_at': str,
             'id': str,
             'spec': ToolchainIntegrationSpec,
-            'status': ToolchainIntegrationStatus
+            'status': ToolchainIntegrationStatus,
+            'updated_at': str
         }
 
         self.attribute_map = {
+            'created_at': 'createdAt',
             'id': 'id',
             'spec': 'spec',
-            'status': 'status'
+            'status': 'status',
+            'updated_at': 'updatedAt'
         }
 
+        self._created_at = created_at
         self._id = id
         self._spec = spec
         self._status = status
+        self._updated_at = updated_at
 
     @classmethod
     def from_dict(cls, dikt) -> 'ToolchainIntegration':
@@ -54,6 +64,29 @@ def from_dict(cls, dikt) -> 'ToolchainIntegration':
         """
         return util.deserialize_model(dikt, cls)
 
+    @property
+    def created_at(self) -> str:
+        """Gets the created_at of this ToolchainIntegration.
+
+        CreatedAt  # noqa: E501
+
+        :return: The created_at of this ToolchainIntegration.
+        :rtype: str
+        """
+        return self._created_at
+
+    @created_at.setter
+    def created_at(self, created_at: str):
+        """Sets the created_at of this ToolchainIntegration.
+
+        CreatedAt  # noqa: E501
+
+        :param created_at: The created_at of this ToolchainIntegration.
+        :type created_at: str
+        """
+
+        self._created_at = created_at
+
     @property
     def id(self) -> str:
         """Gets the id of this ToolchainIntegration.
@@ -122,3 +155,26 @@ def status(self, status: ToolchainIntegrationStatus):
         """
 
         self._status = status
+
+    @property
+    def updated_at(self) -> str:
+        """Gets the updated_at of this ToolchainIntegration.
+
+        UpdatedAt  # noqa: E501
+
+        :return: The updated_at of this ToolchainIntegration.
+        :rtype: str
+        """
+        return self._updated_at
+
+    @updated_at.setter
+    def updated_at(self, updated_at: str):
+        """Sets the updated_at of this ToolchainIntegration.
+
+        UpdatedAt  # noqa: E501
+
+        :param updated_at: The updated_at of this ToolchainIntegration.
+        :type updated_at: str
+        """
+
+        self._updated_at = updated_at
diff --git a/packages/sdk/odahuflow/sdk/models/toolchain_integration_status.py b/packages/sdk/odahuflow/sdk/models/toolchain_integration_status.py
index 952fb712c..81c85760c 100644
--- a/packages/sdk/odahuflow/sdk/models/toolchain_integration_status.py
+++ b/packages/sdk/odahuflow/sdk/models/toolchain_integration_status.py
@@ -15,27 +15,16 @@ class ToolchainIntegrationStatus(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, created_at: str=None, updated_at: str=None):  # noqa: E501
+    def __init__(self):  # noqa: E501
         """ToolchainIntegrationStatus - a model defined in Swagger
 
-        :param created_at: The created_at of this ToolchainIntegrationStatus.  # noqa: E501
-        :type created_at: str
-        :param updated_at: The updated_at of this ToolchainIntegrationStatus.  # noqa: E501
-        :type updated_at: str
         """
         self.swagger_types = {
-            'created_at': str,
-            'updated_at': str
         }
 
         self.attribute_map = {
-            'created_at': 'createdAt',
-            'updated_at': 'updatedAt'
         }
 
-        self._created_at = created_at
-        self._updated_at = updated_at
-
     @classmethod
     def from_dict(cls, dikt) -> 'ToolchainIntegrationStatus':
         """Returns the dict as a model
@@ -46,45 +35,3 @@ def from_dict(cls, dikt) -> 'ToolchainIntegrationStatus':
         :rtype: ToolchainIntegrationStatus
         """
         return util.deserialize_model(dikt, cls)
-
-    @property
-    def created_at(self) -> str:
-        """Gets the created_at of this ToolchainIntegrationStatus.
-
-
-        :return: The created_at of this ToolchainIntegrationStatus.
-        :rtype: str
-        """
-        return self._created_at
-
-    @created_at.setter
-    def created_at(self, created_at: str):
-        """Sets the created_at of this ToolchainIntegrationStatus.
-
-
-        :param created_at: The created_at of this ToolchainIntegrationStatus.
-        :type created_at: str
-        """
-
-        self._created_at = created_at
-
-    @property
-    def updated_at(self) -> str:
-        """Gets the updated_at of this ToolchainIntegrationStatus.
-
-
-        :return: The updated_at of this ToolchainIntegrationStatus.
-        :rtype: str
-        """
-        return self._updated_at
-
-    @updated_at.setter
-    def updated_at(self, updated_at: str):
-        """Sets the updated_at of this ToolchainIntegrationStatus.
-
-
-        :param updated_at: The updated_at of this ToolchainIntegrationStatus.
-        :type updated_at: str
-        """
-
-        self._updated_at = updated_at


Test patch:
diff --git a/packages/operator/controllers/modelpackaging_controller_test.go b/packages/operator/controllers/modelpackaging_controller_test.go
index 1d899e40a..78e4281fa 100644
--- a/packages/operator/controllers/modelpackaging_controller_test.go
+++ b/packages/operator/controllers/modelpackaging_controller_test.go
@@ -132,7 +132,7 @@ func (s *ModelPackagingControllerSuite) SetupTest() {
 		s.g.Expect(mgr.Start(s.stopMgr)).NotTo(HaveOccurred())
 	}()
 
-	if err := s.stubPIClient.CreatePackagingIntegration(s.createPackagingIntegration()); err != nil {
+	if err := s.stubPIClient.SavePackagingIntegration(s.createPackagingIntegration()); err != nil {
 		s.T().Fatal(err)
 	}
 }
diff --git a/packages/operator/pkg/apiserver/routes/v1/connection/connection_routes_test.go b/packages/operator/pkg/apiserver/routes/v1/connection/connection_routes_test.go
index 7eeac12b2..088c614c9 100644
--- a/packages/operator/pkg/apiserver/routes/v1/connection/connection_routes_test.go
+++ b/packages/operator/pkg/apiserver/routes/v1/connection/connection_routes_test.go
@@ -24,6 +24,7 @@ import (
 	"net/http"
 	"net/http/httptest"
 	"strings"
+	"time"
 
 	"github.com/gin-gonic/gin"
 	"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
@@ -384,7 +385,7 @@ func (s *ConnectionRouteGenericSuite) TestCreateConnectionModifiable() {
 	newResourceBody, err := json.Marshal(newResource)
 	s.g.Expect(err).NotTo(HaveOccurred())
 
-	reqTime := routes.GetTimeNowTruncatedToSeconds()
+	reqTime := time.Now()
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodPost, conn_route.CreateConnectionURL, bytes.NewReader(newResourceBody))
 	s.g.Expect(err).NotTo(HaveOccurred())
@@ -395,11 +396,11 @@ func (s *ConnectionRouteGenericSuite) TestCreateConnectionModifiable() {
 	s.g.Expect(err).NotTo(HaveOccurred())
 
 	s.g.Expect(w.Code).Should(Equal(http.StatusCreated))
-	s.g.Expect(resp.Status.CreatedAt).NotTo(BeNil())
-	createdAtWasNotUpdated := reqTime.Before(resp.Status.CreatedAt) || reqTime.Equal(resp.Status.CreatedAt)
+	s.g.Expect(resp.CreatedAt).NotTo(BeNil())
+	createdAtWasNotUpdated := reqTime.Before(resp.CreatedAt) || reqTime.Equal(resp.CreatedAt)
 	s.g.Expect(createdAtWasNotUpdated).Should(Equal(true))
-	s.g.Expect(resp.Status.UpdatedAt).NotTo(BeNil())
-	updatedAtWasUpdated := reqTime.Before(resp.Status.CreatedAt) || reqTime.Equal(resp.Status.CreatedAt)
+	s.g.Expect(resp.UpdatedAt).NotTo(BeNil())
+	updatedAtWasUpdated := reqTime.Before(resp.CreatedAt) || reqTime.Equal(resp.CreatedAt)
 	s.g.Expect(updatedAtWasUpdated).Should(Equal(true))
 }
 
diff --git a/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_test.go b/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_test.go
index 106969186..06a4d9614 100644
--- a/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_test.go
+++ b/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_test.go
@@ -36,6 +36,8 @@ import (
 	mp_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging"
 	mp_postgres_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging/postgres"
 	mp_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/packaging"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/service/packaging_integration"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
 	httputil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/httputil"
 	. "github.com/onsi/gomega"
 	"github.com/stretchr/testify/suite"
@@ -61,13 +63,21 @@ const (
 	testOutConnNotFound = "out-conn-not-found"
 )
 
+type packagingIntegrationService interface {
+	GetPackagingIntegration(id string) (*packaging.PackagingIntegration, error)
+	GetPackagingIntegrationList(options ...filter.ListOption) ([]packaging.PackagingIntegration, error)
+	CreatePackagingIntegration(md *packaging.PackagingIntegration) error
+	UpdatePackagingIntegration(md *packaging.PackagingIntegration) error
+	DeletePackagingIntegration(id string) error
+}
+
 type ModelPackagingRouteSuite struct {
 	suite.Suite
 	g              *GomegaWithT
 	server         *gin.Engine
 	packRepo       mp_repository.Repository
 	packService    mp_service.Service
-	piRepo         mp_repository.PackagingIntegrationRepository
+	piService      packagingIntegrationService
 	kubePackClient kube_client.Client
 	connStorage    conn_repository.Repository
 	k8sClient      client.Client
@@ -77,11 +87,12 @@ func (s *ModelPackagingRouteSuite) SetupSuite() {
 
 	s.k8sClient = kubeClient
 	s.kubePackClient = kube_client.NewClient(testNamespace, testNamespace, s.k8sClient, cfg)
-	s.piRepo = mp_postgres_repository.PackagingIntegrationRepository{DB: db}
+	piRepo := mp_postgres_repository.PackagingIntegrationRepository{DB: db}
+	s.piService = packaging_integration.NewService(&piRepo)
 	s.packRepo = mp_postgres_repository.PackagingRepo{DB: db}
 	s.packService = mp_service.NewService(s.packRepo)
 
-	err := s.piRepo.CreatePackagingIntegration(&packaging.PackagingIntegration{
+	err := s.piService.CreatePackagingIntegration(&packaging.PackagingIntegration{
 		ID: piIDMpRoute,
 		Spec: packaging.PackagingIntegrationSpec{
 			Entrypoint:   piEntrypointMpRoute,
@@ -95,7 +106,7 @@ func (s *ModelPackagingRouteSuite) SetupSuite() {
 
 	s.connStorage = conn_k8s_repository.NewRepository(testNamespace, s.k8sClient)
 	// Create the connection that will be used as the outputConnection param for a training.
-	if err := s.connStorage.CreateConnection(&connection.Connection{
+	if err := s.connStorage.SaveConnection(&connection.Connection{
 		ID: testOutConn,
 		Spec: odahuflowv1alpha1.ConnectionSpec{
 			Type: connection.GcsType,
@@ -105,7 +116,7 @@ func (s *ModelPackagingRouteSuite) SetupSuite() {
 	}
 
 	// Create the connection that will be used as the default outputConnection param for a training.
-	if err := s.connStorage.CreateConnection(&connection.Connection{
+	if err := s.connStorage.SaveConnection(&connection.Connection{
 		ID: testOutConnDefault,
 		Spec: odahuflowv1alpha1.ConnectionSpec{
 			Type: connection.GcsType,
@@ -116,7 +127,7 @@ func (s *ModelPackagingRouteSuite) SetupSuite() {
 }
 
 func (s *ModelPackagingRouteSuite) TearDownSuite() {
-	if err := s.piRepo.DeletePackagingIntegration(piIDMpValid); err != nil {
+	if err := s.piService.DeletePackagingIntegration(piIDMpValid); err != nil {
 		s.T().Fatal(err)
 	}
 
@@ -135,7 +146,7 @@ func (s *ModelPackagingRouteSuite) registerHandlers(packagingConfig config.Model
 
 	pack_route.ConfigureRoutes(
 		packGroup, s.kubePackClient, s.packService,
-		s.piRepo, s.connStorage, packagingConfig,
+		s.piService, s.connStorage, packagingConfig,
 		config.NvidiaResourceName,
 	)
 }
diff --git a/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_validation_test.go b/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_validation_test.go
index df09a04c9..3d60ac05d 100644
--- a/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_validation_test.go
+++ b/packages/operator/pkg/apiserver/routes/v1/packaging/model_packaging_validation_test.go
@@ -19,6 +19,7 @@ package packaging_test
 import (
 	"fmt"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/service/packaging_integration"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/validation"
 	"go.uber.org/multierr"
 	"testing"
@@ -94,7 +95,7 @@ var (
 	}
 	validNodeSelector = map[string]string{"mode": "valid"}
 	validPackaging    = packaging.ModelPackaging{
-	    ID: "valid-id",
+		ID: "valid-id",
 		Spec: packaging.ModelPackagingSpec{
 			IntegrationName:  piIDMpValid,
 			ArtifactName:     "test",
@@ -118,7 +119,7 @@ type ModelPackagingValidationSuite struct {
 	g            *GomegaWithT
 	mpKubeClient kube_client.Client
 	mpRepo       mp_repository.Repository
-	mpiRepo      mp_repository.PackagingIntegrationRepository
+	piService    packagingIntegrationService
 	connRepo     conn_repository.Repository
 	validator    *pack_route.MpValidator
 }
@@ -132,7 +133,8 @@ func (s *ModelPackagingValidationSuite) SetupSuite() {
 	s.mpKubeClient = kube_client.NewClient(testNamespace, testNamespace, kubeClient, cfg)
 
 	s.mpRepo = mp_post_repository.PackagingRepo{DB: db}
-	s.mpiRepo = mp_post_repository.PackagingIntegrationRepository{DB: db}
+	piRepo := mp_post_repository.PackagingIntegrationRepository{DB: db}
+	s.piService = packaging_integration.NewService(&piRepo)
 
 	s.connRepo = conn_k8s_repository.NewRepository(testNamespace, kubeClient)
 
@@ -140,13 +142,13 @@ func (s *ModelPackagingValidationSuite) SetupSuite() {
 	packagingConfig.NodePools = append(packagingConfig.NodePools, config.NodePool{NodeSelector: validNodeSelector})
 
 	s.validator = pack_route.NewMpValidator(
-		s.mpiRepo,
+		s.piService,
 		s.connRepo,
 		packagingConfig,
 		config.NvidiaResourceName,
 	)
 
-	err := s.mpiRepo.CreatePackagingIntegration(&packaging.PackagingIntegration{
+	err := s.piService.CreatePackagingIntegration(&packaging.PackagingIntegration{
 		ID: piIDMpValid,
 		Spec: packaging.PackagingIntegrationSpec{
 			Entrypoint:   piEntrypointMpValid,
@@ -161,7 +163,7 @@ func (s *ModelPackagingValidationSuite) SetupSuite() {
 		s.T().Fatal(err)
 	}
 
-	err = s.connRepo.CreateConnection(&connection.Connection{
+	err = s.connRepo.SaveConnection(&connection.Connection{
 		ID: connDockerTypeMpValid,
 		Spec: v1alpha1.ConnectionSpec{
 			Type: connection.DockerType,
@@ -172,7 +174,7 @@ func (s *ModelPackagingValidationSuite) SetupSuite() {
 		s.T().Fatal(err)
 	}
 
-	err = s.connRepo.CreateConnection(&connection.Connection{
+	err = s.connRepo.SaveConnection(&connection.Connection{
 		ID: connS3TypeMpValid,
 		Spec: v1alpha1.ConnectionSpec{
 			Type: connection.S3Type,
@@ -193,7 +195,7 @@ func (s *ModelPackagingValidationSuite) TearDownSuite() {
 		s.T().Fatal(err)
 	}
 
-	if err := s.mpiRepo.DeletePackagingIntegration(piIDMpValid); err != nil {
+	if err := s.piService.DeletePackagingIntegration(piIDMpValid); err != nil {
 		s.T().Fatal(err)
 	}
 }
@@ -352,7 +354,7 @@ func (s *ModelPackagingValidationSuite) TestMpRequiredTargets() {
 
 func (s *ModelPackagingValidationSuite) TestMpDefaultTargets() {
 	ti := &packaging.ModelPackaging{
-	    ID: "valid-id",
+		ID: "valid-id",
 		Spec: packaging.ModelPackagingSpec{
 			IntegrationName:  piIDMpValid,
 			ArtifactName:     "test",
@@ -496,7 +498,7 @@ func (s *ModelPackagingValidationSuite) TestOutputConnection() {
 
 	// If configuration output connection is not set then user must specify it as ModelTraining parameter
 	err := pack_route.NewMpValidator(
-		s.mpiRepo,
+		s.piService,
 		s.connRepo,
 		config.NewDefaultModelPackagingConfig(),
 		config.NvidiaResourceName,
@@ -509,7 +511,7 @@ func (s *ModelPackagingValidationSuite) TestOutputConnection() {
 	packConfig := config.NewDefaultModelPackagingConfig()
 	packConfig.OutputConnectionID = testMpOutConnDefault
 	_ = pack_route.NewMpValidator(
-		s.mpiRepo,
+		s.piService,
 		s.connRepo,
 		packConfig,
 		config.NvidiaResourceName,
@@ -522,7 +524,7 @@ func (s *ModelPackagingValidationSuite) TestOutputConnection() {
 	packConfig.OutputConnectionID = "default-output-connection"
 	mp.Spec.OutputConnection = testMpOutConn
 	_ = pack_route.NewMpValidator(
-		s.mpiRepo,
+		s.piService,
 		s.connRepo,
 		config.NewDefaultModelPackagingConfig(),
 		config.NvidiaResourceName,
@@ -534,7 +536,7 @@ func (s *ModelPackagingValidationSuite) TestOutputConnection() {
 		Spec: packaging.ModelPackagingSpec{OutputConnection: testMpOutConnNotFound},
 	}
 	err = pack_route.NewMpValidator(
-		s.mpiRepo,
+		s.piService,
 		s.connRepo,
 		config.NewDefaultModelPackagingConfig(),
 		config.NvidiaResourceName,
diff --git a/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration_postgres_backend_test.go b/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration_postgres_backend_test.go
index f3b342a05..029e5ed13 100644
--- a/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration_postgres_backend_test.go
+++ b/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration_postgres_backend_test.go
@@ -18,7 +18,7 @@ package packaging_test
 
 import (
 	_ "github.com/lib/pq"
-	mp_postgres_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging/postgres"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/service/packaging_integration"
 	"github.com/stretchr/testify/suite"
 	"testing"
 )
@@ -28,11 +28,9 @@ type PIPostgresRouteSuite struct {
 }
 
 func (s *PIPostgresRouteSuite) SetupSuite() {
-
-	s.mpRepository = mp_postgres_repository.PackagingIntegrationRepository{DB: db}
+	s.piServiceMock = &packaging_integration.MockService{}
 }
 
-
 func TestPIPostgresRouteSuite(t *testing.T) {
 	suite.Run(t, new(PIPostgresRouteSuite))
 }
diff --git a/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration_test.go b/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration_test.go
index d12a82afa..1287fe2b4 100644
--- a/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration_test.go
+++ b/packages/operator/pkg/apiserver/routes/v1/packaging/packaging_integration_test.go
@@ -20,23 +20,22 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
-	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
-	httputil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/httputil"
-	"net/http"
-	"net/http/httptest"
-	"time"
-
 	"github.com/gin-gonic/gin"
 	"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/connection"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/packaging"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes"
 	pack_route "github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/packaging"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
 	odahuErrors "github.com/odahu/odahu-flow/packages/operator/pkg/errors"
-	mp_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/service/packaging_integration"
+	httputil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/httputil"
 	. "github.com/onsi/gomega"
+	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/suite"
-	"k8s.io/apimachinery/pkg/api/errors"
+	"net/http"
+	"net/http/httptest"
+	"testing"
 )
 
 const (
@@ -92,13 +91,18 @@ var (
 
 type PackagingIntegrationRouteSuite struct {
 	suite.Suite
-	g            *GomegaWithT
-	server       *gin.Engine
-	mpRepository mp_repository.PackagingIntegrationRepository
+	g             *GomegaWithT
+	server        *gin.Engine
+	piServiceMock *packaging_integration.MockService
+}
+
+func TestPIGenericRouteSuite(t *testing.T) {
+	suite.Run(t, new(PackagingIntegrationRouteSuite))
 }
 
 func (s *PackagingIntegrationRouteSuite) SetupTest() {
 	s.g = NewGomegaWithT(s.T())
+	s.piServiceMock = &packaging_integration.MockService{}
 	s.registerHandlers(config.NewDefaultModelPackagingConfig())
 }
 
@@ -106,16 +110,7 @@ func (s *PackagingIntegrationRouteSuite) registerHandlers(packagingConfig config
 	s.server = gin.Default()
 	v1Group := s.server.Group("")
 	packGroup := v1Group.Group("", routes.DisableAPIMiddleware(packagingConfig.Enabled))
-	pack_route.ConfigurePiRoutes(packGroup, s.mpRepository)
-}
-
-func (s *PackagingIntegrationRouteSuite) TearDownTest() {
-	for _, piID := range []string{piID} {
-		err := s.mpRepository.DeletePackagingIntegration(piID)
-		if err != nil && !errors.IsNotFound(err) && !odahuErrors.IsNotFoundError(err) {
-			s.T().Fail()
-		}
-	}
+	pack_route.ConfigurePiRoutes(packGroup, s.piServiceMock)
 }
 
 func newPackagingIntegration() *packaging.PackagingIntegration {
@@ -135,7 +130,7 @@ func newPackagingIntegration() *packaging.PackagingIntegration {
 
 func (s *PackagingIntegrationRouteSuite) TestGetPackagingIntegration() {
 	pi := newPackagingIntegration()
-	s.g.Expect(s.mpRepository.CreatePackagingIntegration(pi)).NotTo(HaveOccurred())
+	s.piServiceMock.On("GetPackagingIntegration", pi.ID).Return(pi, nil)
 
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/packaging/integration/%s", piID), nil)
@@ -151,8 +146,13 @@ func (s *PackagingIntegrationRouteSuite) TestGetPackagingIntegration() {
 }
 
 func (s *PackagingIntegrationRouteSuite) TestGetPackagingIntegrationNotFound() {
+	notFoundID := "not-found"
+	s.piServiceMock.
+		On("GetPackagingIntegration", notFoundID).
+		Return(nil, odahuErrors.NotFoundError{})
+
 	w := httptest.NewRecorder()
-	req, err := http.NewRequest(http.MethodGet, "/packaging/integration/not-found", nil)
+	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/packaging/integration/%s", notFoundID), nil)
 	s.g.Expect(err).NotTo(HaveOccurred())
 	s.server.ServeHTTP(w, req)
 
@@ -166,6 +166,9 @@ func (s *PackagingIntegrationRouteSuite) TestGetPackagingIntegrationNotFound() {
 
 func (s *PackagingIntegrationRouteSuite) TestCreatePackagingIntegration() {
 	piEntity := newPackagingIntegration()
+
+	s.piServiceMock.On("CreatePackagingIntegration", piEntity).Return(nil)
+
 	piEntityBody, err := json.Marshal(piEntity)
 	s.g.Expect(err).NotTo(HaveOccurred())
 
@@ -181,43 +184,13 @@ func (s *PackagingIntegrationRouteSuite) TestCreatePackagingIntegration() {
 	s.g.Expect(w.Code).Should(Equal(http.StatusCreated))
 	s.g.Expect(piResponse.ID).Should(Equal(piEntity.ID))
 	s.g.Expect(piResponse.Spec).Should(Equal(piEntity.Spec))
-
-	pi, err := s.mpRepository.GetPackagingIntegration(piID)
-	s.g.Expect(err).ShouldNot(HaveOccurred())
-	s.g.Expect(pi.Spec).To(Equal(piEntity.Spec))
-}
-
-// CreatedAt and UpdatedAt field should automatically be updated after create request
-func (s *PackagingIntegrationRouteSuite) TestCreatePackagingIntegrationModifiable() {
-	newResource := newPackagingIntegration()
-
-	newResourceBody, err := json.Marshal(newResource)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	reqTime := routes.GetTimeNowTruncatedToSeconds()
-	w := httptest.NewRecorder()
-	req, err := http.NewRequest(
-		http.MethodPost, pack_route.CreatePackagingIntegrationURL, bytes.NewReader(newResourceBody),
-	)
-	s.g.Expect(err).NotTo(HaveOccurred())
-	s.server.ServeHTTP(w, req)
-
-	var resp packaging.ModelPackaging
-	err = json.Unmarshal(w.Body.Bytes(), &resp)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	s.g.Expect(w.Code).Should(Equal(http.StatusCreated))
-	s.g.Expect(resp.Status.CreatedAt).NotTo(BeNil())
-	createdAtWasNotUpdated := reqTime.Before(resp.Status.CreatedAt) || reqTime.Equal(resp.Status.CreatedAt)
-	s.g.Expect(createdAtWasNotUpdated).Should(Equal(true))
-	s.g.Expect(resp.Status.UpdatedAt).NotTo(BeNil())
-	updatedAtWasUpdated := reqTime.Before(resp.Status.CreatedAt) || reqTime.Equal(resp.Status.CreatedAt)
-	s.g.Expect(updatedAtWasUpdated).Should(Equal(true))
 }
 
 func (s *PackagingIntegrationRouteSuite) TestCreateDuplicatePackagingIntegration() {
 	pi := newPackagingIntegration()
-	s.g.Expect(s.mpRepository.CreatePackagingIntegration(pi)).NotTo(HaveOccurred())
+	s.piServiceMock.
+		On("CreatePackagingIntegration", pi).
+		Return(odahuErrors.AlreadyExistError{})
 
 	piEntityBody, err := json.Marshal(pi)
 	s.g.Expect(err).NotTo(HaveOccurred())
@@ -236,11 +209,9 @@ func (s *PackagingIntegrationRouteSuite) TestCreateDuplicatePackagingIntegration
 }
 
 func (s *PackagingIntegrationRouteSuite) TestUpdatePackagingIntegration() {
-	pi := newPackagingIntegration()
-	s.g.Expect(s.mpRepository.CreatePackagingIntegration(pi)).NotTo(HaveOccurred())
-
 	piEntity := newPackagingIntegration()
-	piEntity.Spec.Entrypoint = "new-entrypoint"
+
+	s.piServiceMock.On("UpdatePackagingIntegration", piEntity).Return(nil)
 
 	piEntityBody, err := json.Marshal(piEntity)
 	s.g.Expect(err).NotTo(HaveOccurred())
@@ -257,45 +228,13 @@ func (s *PackagingIntegrationRouteSuite) TestUpdatePackagingIntegration() {
 	s.g.Expect(w.Code).Should(Equal(http.StatusOK))
 	s.g.Expect(piResponse.ID).Should(Equal(piEntity.ID))
 	s.g.Expect(piResponse.Spec).Should(Equal(piEntity.Spec))
-
-	pi, err = s.mpRepository.GetPackagingIntegration(piID)
-	s.g.Expect(err).NotTo(HaveOccurred())
-	s.g.Expect(pi.Spec).To(Equal(piEntity.Spec))
-}
-
-// UpdatedAt field should automatically be updated after update request
-func (s *PackagingIntegrationRouteSuite) TestUpdatePackagingIntegrationModifiable() {
-	resource := newPackagingIntegration()
-	s.g.Expect(s.mpRepository.CreatePackagingIntegration(resource)).NotTo(HaveOccurred())
-
-	time.Sleep(1 * time.Second)
-
-	newResource := newPackagingIntegration()
-
-	newResourceBody, err := json.Marshal(newResource)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	reqTime := routes.GetTimeNowTruncatedToSeconds()
-	w := httptest.NewRecorder()
-	req, err := http.NewRequest(http.MethodPut, pack_route.UpdatePackagingIntegrationURL, bytes.NewReader(newResourceBody))
-	s.g.Expect(err).NotTo(HaveOccurred())
-	s.server.ServeHTTP(w, req)
-
-	var respResource packaging.ModelPackaging
-	err = json.Unmarshal(w.Body.Bytes(), &respResource)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	s.g.Expect(w.Code).Should(Equal(http.StatusOK))
-	s.g.Expect(respResource.Status.CreatedAt).NotTo(BeNil())
-	createdAtWasNotUpdated := reqTime.After(respResource.Status.CreatedAt.Time)
-	s.g.Expect(createdAtWasNotUpdated).Should(Equal(true))
-	s.g.Expect(respResource.Status.UpdatedAt).NotTo(BeNil())
-	updatedAtWasUpdated := reqTime.Before(respResource.Status.UpdatedAt) || reqTime.Equal(respResource.Status.UpdatedAt)
-	s.g.Expect(updatedAtWasUpdated).Should(Equal(true))
 }
 
 func (s *PackagingIntegrationRouteSuite) TestUpdatePackagingIntegrationNotFound() {
 	pi := newPackagingIntegration()
+
+	s.piServiceMock.On("UpdatePackagingIntegration", pi).Return(odahuErrors.NotFoundError{})
+
 	piEntityBody, err := json.Marshal(pi)
 	s.g.Expect(err).NotTo(HaveOccurred())
 
@@ -314,7 +253,7 @@ func (s *PackagingIntegrationRouteSuite) TestUpdatePackagingIntegrationNotFound(
 
 func (s *PackagingIntegrationRouteSuite) TestDeletePackagingIntegration() {
 	pi := newPackagingIntegration()
-	s.g.Expect(s.mpRepository.CreatePackagingIntegration(pi)).NotTo(HaveOccurred())
+	s.piServiceMock.On("DeletePackagingIntegration", pi.ID).Return(nil)
 
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("/packaging/integration/%s", piID), nil)
@@ -327,13 +266,11 @@ func (s *PackagingIntegrationRouteSuite) TestDeletePackagingIntegration() {
 
 	s.g.Expect(w.Code).Should(Equal(http.StatusOK))
 	s.g.Expect(result.Message).Should(ContainSubstring("was deleted"))
-
-	piList, err := s.mpRepository.GetPackagingIntegrationList()
-	s.g.Expect(err).NotTo(HaveOccurred())
-	s.g.Expect(piList).To(HaveLen(0))
 }
 
 func (s *PackagingIntegrationRouteSuite) TestDeletePackagingIntegrationNotFound() {
+	s.piServiceMock.On("DeletePackagingIntegration", piID).Return(odahuErrors.NotFoundError{})
+
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("/packaging/integration/%s", piID), nil)
 	s.g.Expect(err).NotTo(HaveOccurred())
@@ -347,13 +284,14 @@ func (s *PackagingIntegrationRouteSuite) TestDeletePackagingIntegrationNotFound(
 	s.g.Expect(result.Message).Should(ContainSubstring("not found"))
 }
 
+// GET methods still work despite disabled API
 func (s *PackagingIntegrationRouteSuite) TestDisabledAPIGetPackagingIntegration() {
 	packagingConfig := config.NewDefaultModelPackagingConfig()
 	packagingConfig.Enabled = false
 	s.registerHandlers(packagingConfig)
 
 	pi := newPackagingIntegration()
-	s.g.Expect(s.mpRepository.CreatePackagingIntegration(pi)).NotTo(HaveOccurred())
+	s.piServiceMock.On("GetPackagingIntegration", pi.ID).Return(pi, nil)
 
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/packaging/integration/%s", piID), nil)
@@ -373,20 +311,20 @@ func (s *PackagingIntegrationRouteSuite) TestDisabledAPIGetAllPackagingIntegrati
 	packagingConfig.Enabled = false
 	s.registerHandlers(packagingConfig)
 
-	pi := newPackagingIntegration()
-	s.g.Expect(s.mpRepository.CreatePackagingIntegration(pi)).NotTo(HaveOccurred())
+	piList := []packaging.PackagingIntegration{*newPackagingIntegration()}
+	s.piServiceMock.On("GetPackagingIntegrationList", mock.Anything, mock.Anything).Return(piList, nil)
 
 	w := httptest.NewRecorder()
-	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/packaging/integration/%s", piID), nil)
+	req, err := http.NewRequest(http.MethodGet, "/packaging/integration", nil)
 	s.g.Expect(err).NotTo(HaveOccurred())
 	s.server.ServeHTTP(w, req)
 
-	var result packaging.PackagingIntegration
+	var result []packaging.PackagingIntegration
 	err = json.Unmarshal(w.Body.Bytes(), &result)
 	s.g.Expect(err).NotTo(HaveOccurred())
 
 	s.g.Expect(w.Code).Should(Equal(http.StatusOK))
-	s.g.Expect(result.Spec).Should(Equal(pi.Spec))
+	s.g.Expect(result).Should(Equal(piList))
 }
 
 func (s *PackagingIntegrationRouteSuite) TestDisabledAPICreatePackagingIntegration() {
@@ -395,7 +333,6 @@ func (s *PackagingIntegrationRouteSuite) TestDisabledAPICreatePackagingIntegrati
 	s.registerHandlers(packagingConfig)
 
 	pi := newPackagingIntegration()
-	s.g.Expect(s.mpRepository.CreatePackagingIntegration(pi)).NotTo(HaveOccurred())
 
 	piEntityBody, err := json.Marshal(pi)
 	s.g.Expect(err).NotTo(HaveOccurred())
diff --git a/packages/operator/pkg/apiserver/routes/v1/training/model_training_test.go b/packages/operator/pkg/apiserver/routes/v1/training/model_training_test.go
index fdb3b632a..a74f7b770 100644
--- a/packages/operator/pkg/apiserver/routes/v1/training/model_training_test.go
+++ b/packages/operator/pkg/apiserver/routes/v1/training/model_training_test.go
@@ -33,8 +33,8 @@ import (
 	"github.com/odahu/odahu-flow/packages/operator/pkg/odahuflow"
 	conn_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
 	conn_k8s_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection/kubernetes"
-	mt_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training"
 	mt_postgres_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training/postgres"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/service/toolchain"
 	mt_service "github.com/odahu/odahu-flow/packages/operator/pkg/service/training"
 	httputil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/httputil"
 	. "github.com/onsi/gomega"
@@ -49,16 +49,22 @@ import (
 	"testing"
 )
 
+type toolchainService interface {
+	GetToolchainIntegration(name string) (*training.ToolchainIntegration, error)
+	CreateToolchainIntegration(md *training.ToolchainIntegration) error
+	DeleteToolchainIntegration(name string) error
+}
+
 type ModelTrainingRouteSuite struct {
 	suite.Suite
 	g      *GomegaWithT
 	server *gin.Engine
 
-	trainService    mt_service.Service
-	toolchainRepo   mt_repository.ToolchainRepository
-	connRepo        conn_repository.Repository
-	kubeTrainClient kube_client.Client
-	k8sClient       client.Client
+	trainService     mt_service.Service
+	toolchainService toolchainService
+	connRepo         conn_repository.Repository
+	kubeTrainClient  kube_client.Client
+	k8sClient        client.Client
 }
 
 func (s *ModelTrainingRouteSuite) SetupSuite() {
@@ -66,13 +72,16 @@ func (s *ModelTrainingRouteSuite) SetupSuite() {
 	s.k8sClient = kubeClient
 
 	s.trainService = mt_service.NewService(mt_postgres_repository.TrainingRepo{DB: db})
-	s.toolchainRepo = mt_postgres_repository.ToolchainRepo{DB: db}
+
+	tiRepo := mt_postgres_repository.ToolchainRepo{DB: db}
+	s.toolchainService = toolchain.NewService(tiRepo)
+
 	s.kubeTrainClient = kube_client.NewClient(testNamespace, testNamespace, s.k8sClient, cfg)
 
 	s.connRepo = conn_k8s_repository.NewRepository(testNamespace, s.k8sClient)
 
 	// Create the connection that will be used as the vcs param for a training.
-	if err := s.connRepo.CreateConnection(&connection.Connection{
+	if err := s.connRepo.SaveConnection(&connection.Connection{
 		ID: testMtVCSID,
 		Spec: odahuflowv1alpha1.ConnectionSpec{
 			Type:      connection.GITType,
@@ -84,7 +93,7 @@ func (s *ModelTrainingRouteSuite) SetupSuite() {
 	}
 
 	// Create the toolchain integration that will be used for a training.
-	if err := s.toolchainRepo.CreateToolchainIntegration(&training.ToolchainIntegration{
+	if err := s.toolchainService.CreateToolchainIntegration(&training.ToolchainIntegration{
 		ID: testToolchainIntegrationID,
 		Spec: odahuflowv1alpha1.ToolchainIntegrationSpec{
 			DefaultImage: testToolchainMtImage,
@@ -95,7 +104,7 @@ func (s *ModelTrainingRouteSuite) SetupSuite() {
 	}
 
 	// Create the connection that will be used as the outputConnection param for a training.
-	if err := s.connRepo.CreateConnection(&connection.Connection{
+	if err := s.connRepo.SaveConnection(&connection.Connection{
 		ID: testMtOutConn,
 		Spec: odahuflowv1alpha1.ConnectionSpec{
 			Type: connection.GcsType,
@@ -106,7 +115,7 @@ func (s *ModelTrainingRouteSuite) SetupSuite() {
 	}
 
 	// Create the connection that will be used as the default outputConnection param for a training.
-	if err := s.connRepo.CreateConnection(&connection.Connection{
+	if err := s.connRepo.SaveConnection(&connection.Connection{
 		ID: testMtOutConnDefault,
 		Spec: odahuflowv1alpha1.ConnectionSpec{
 			Type: connection.GcsType,
@@ -121,7 +130,7 @@ func (s *ModelTrainingRouteSuite) TearDownSuite() {
 
 	var errs []error
 
-	if err := s.toolchainRepo.DeleteToolchainIntegration(testToolchainIntegrationID); err != nil {
+	if err := s.toolchainService.DeleteToolchainIntegration(testToolchainIntegrationID); err != nil {
 		errs = append(errs, err)
 	}
 
@@ -160,7 +169,7 @@ func (s *ModelTrainingRouteSuite) registerHandlers(trainingConfig config.ModelTr
 
 	train_route.ConfigureRoutes(
 		trainGroup, trainingConfig, config.NvidiaResourceName,
-		s.trainService, s.toolchainRepo, s.connRepo, s.kubeTrainClient)
+		s.trainService, s.toolchainService, s.connRepo, s.kubeTrainClient)
 }
 
 func (s *ModelTrainingRouteSuite) newMultipleMtStubs() {
diff --git a/packages/operator/pkg/apiserver/routes/v1/training/model_training_validation_test.go b/packages/operator/pkg/apiserver/routes/v1/training/model_training_validation_test.go
index bf5424756..51483d771 100644
--- a/packages/operator/pkg/apiserver/routes/v1/training/model_training_validation_test.go
+++ b/packages/operator/pkg/apiserver/routes/v1/training/model_training_validation_test.go
@@ -25,8 +25,8 @@ import (
 	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
 	conn_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection"
 	conn_k8s_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/connection/kubernetes"
-	mt_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training"
 	mt_post_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training/postgres"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/service/toolchain"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/validation"
 	. "github.com/onsi/gomega"
 	"github.com/stretchr/testify/suite"
@@ -70,7 +70,7 @@ type ModelTrainingValidationSuite struct {
 	suite.Suite
 	g              *GomegaWithT
 	validator      *train_route.MtValidator
-	mtRepository   mt_repository.ToolchainRepository
+	tiService      toolchainService
 	connRepository conn_repository.Repository
 }
 
@@ -80,7 +80,8 @@ func (s *ModelTrainingValidationSuite) SetupTest() {
 
 func (s *ModelTrainingValidationSuite) SetupSuite() {
 
-	s.mtRepository = mt_post_repository.ToolchainRepo{DB: db}
+	tiRepo := mt_post_repository.ToolchainRepo{DB: db}
+	s.tiService = toolchain.NewService(tiRepo)
 	s.connRepository = conn_k8s_repository.NewRepository(testNamespace, kubeClient)
 
 	trainingConfig := config.NewDefaultModelTrainingConfig()
@@ -91,14 +92,14 @@ func (s *ModelTrainingValidationSuite) SetupSuite() {
 	})
 
 	s.validator = train_route.NewMtValidator(
-		s.mtRepository,
+		s.tiService,
 		s.connRepository,
 		trainingConfig,
 		gpuResourceName,
 	)
 
 	// Create the connection that will be used as the vcs param for a training.
-	if err := s.connRepository.CreateConnection(&connection.Connection{
+	if err := s.connRepository.SaveConnection(&connection.Connection{
 		ID: testMtVCSID,
 		Spec: v1alpha1.ConnectionSpec{
 			Type:      connection.GITType,
@@ -110,7 +111,7 @@ func (s *ModelTrainingValidationSuite) SetupSuite() {
 	}
 
 	// Create the connection that will be used as the outputConnection param for a training.
-	if err := s.connRepository.CreateConnection(&connection.Connection{
+	if err := s.connRepository.SaveConnection(&connection.Connection{
 		ID: mtvOutputConnection,
 		Spec: v1alpha1.ConnectionSpec{
 			Type: connection.GcsType,
@@ -121,7 +122,7 @@ func (s *ModelTrainingValidationSuite) SetupSuite() {
 	}
 
 	// Create the toolchain integration that will be used for a training.
-	if err := s.mtRepository.CreateToolchainIntegration(&training.ToolchainIntegration{
+	if err := s.tiService.CreateToolchainIntegration(&training.ToolchainIntegration{
 		ID: testToolchainIntegrationID,
 		Spec: v1alpha1.ToolchainIntegrationSpec{
 			DefaultImage: testToolchainMtImage,
@@ -133,7 +134,7 @@ func (s *ModelTrainingValidationSuite) SetupSuite() {
 }
 
 func (s *ModelTrainingValidationSuite) TearDownSuite() {
-	if err := s.mtRepository.DeleteToolchainIntegration(testToolchainIntegrationID); err != nil {
+	if err := s.tiService.DeleteToolchainIntegration(testToolchainIntegrationID); err != nil {
 		panic(err)
 	}
 
@@ -221,7 +222,7 @@ func (s *ModelTrainingValidationSuite) TestMtNotExplicitMTReference() {
 		},
 	}
 
-	err := s.connRepository.CreateConnection(conn)
+	err := s.connRepository.SaveConnection(conn)
 	s.g.Expect(err).Should(BeNil())
 	defer s.connRepository.DeleteConnection(conn.ID)
 
@@ -250,7 +251,7 @@ func (s *ModelTrainingValidationSuite) TestMtWrongVcsConnectionType() {
 		},
 	}
 
-	err := s.connRepository.CreateConnection(conn)
+	err := s.connRepository.SaveConnection(conn)
 	s.g.Expect(err).Should(BeNil())
 	defer s.connRepository.DeleteConnection(conn.ID)
 
@@ -277,7 +278,7 @@ func (s *ModelTrainingValidationSuite) TestMtWrongObjectStorageConnectionType()
 		},
 	}
 
-	err := s.connRepository.CreateConnection(conn)
+	err := s.connRepository.SaveConnection(conn)
 	s.g.Expect(err).Should(BeNil())
 	defer s.connRepository.DeleteConnection(conn.ID)
 
@@ -565,7 +566,7 @@ func (s *ModelTrainingValidationSuite) TestOutputConnection_NoDefault_NoParam()
 		OutputConnectionID: "",
 	}
 	err := train_route.NewMtValidator(
-		s.mtRepository,
+		s.tiService,
 		s.connRepository,
 		testConfig,
 		gpuResourceName,
@@ -580,7 +581,7 @@ func (s *ModelTrainingValidationSuite) TestOutputConnection_Default_NoParam() {
 
 	testConfig := config.ModelTrainingConfig{OutputConnectionID: testMtOutConnDefault}
 	_ = train_route.NewMtValidator(
-		s.mtRepository,
+		s.tiService,
 		s.connRepository,
 		testConfig,
 		gpuResourceName,
@@ -596,7 +597,7 @@ func (s *ModelTrainingValidationSuite) TestOutputConnection_Both_Default_Param()
 	s.g.Expect(mt.Spec.OutputConnection).Should(Equal(mtvOutputConnection))
 }
 
-// If connection repository doesn't contain connection with passed ID validation must raise NotFoundError
+// If connection service doesn't contain connection with passed ID validation must raise NotFoundError
 func (s *ModelTrainingValidationSuite) TestOutputConnection_ConnectionNotFound() {
 	mt := &training.ModelTraining{
 		Spec: v1alpha1.ModelTrainingSpec{OutputConnection: testMpOutConnNotFound},
diff --git a/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration_postgres_backend_test.go b/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration_postgres_backend_test.go
deleted file mode 100644
index b4f256897..000000000
--- a/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration_postgres_backend_test.go
+++ /dev/null
@@ -1,107 +0,0 @@
-//
-//    Copyright 2020 EPAM Systems
-//
-//    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 training_test
-
-import (
-	"database/sql"
-	"fmt"
-	_ "github.com/lib/pq"
-	migrator_package "github.com/odahu/odahu-flow/packages/operator/pkg/database/migrators/postgres"
-	mt_postgres_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training/postgres"
-	"github.com/ory/dockertest"
-	dc "github.com/ory/dockertest/docker"
-	"github.com/stretchr/testify/suite"
-	"testing"
-	"time"
-)
-
-type TIPostgresRouteSuite struct {
-	TIGenericRouteSuite
-	pool     *dockertest.Pool
-	resource *dockertest.Resource
-}
-
-func (s *TIPostgresRouteSuite) SetupSuite() {
-
-	// Init postgres repo
-
-	var err error
-
-	s.pool, err = dockertest.NewPool("")
-	if err != nil {
-		panic(err)
-	}
-
-	s.pool.MaxWait = time.Second * 30
-
-	// pulls an image, creates a container based on it and runs it
-	s.resource, err = s.pool.RunWithOptions(&dockertest.RunOptions{
-		Repository: "postgres",
-		Tag:        "12",
-		PortBindings: map[dc.Port][]dc.PortBinding{
-			"5432": {
-				{HostIP: "0.0.0.0", HostPort: "5432"},
-			},
-		},
-		Env: []string{"POSTGRES_PASSWORD=example"},
-	})
-	if err != nil {
-		panic("Could not start postgres")
-	}
-	if err = s.resource.Expire(5 * 60); err != nil {
-		panic("Could not set container expire")
-	}
-
-	connString := fmt.Sprintf(
-		"postgresql://postgres:example@localhost:%s/postgres?sslmode=disable",
-		s.resource.GetPort("5432/tcp"),
-	)
-
-	// exponential backoff-retry, because the application in the container might not be ready to accept connections yet
-
-	var db = &sql.DB{}
-
-	if err := s.pool.Retry(func() error {
-		var err error
-		db, err = sql.Open("postgres", connString)
-		if err != nil {
-			return err
-		}
-		return db.Ping()
-	}); err != nil {
-		panic("Cannot connect to DB")
-	}
-
-	migrator, err := migrator_package.NewMigrator(connString)
-	if err != nil {
-		panic("Cannot create migrator")
-	}
-	err = migrator.MigrateToLatest()
-	if err != nil {
-		panic("Cannot migrate schema")
-	}
-
-	s.mtRepository = mt_postgres_repository.ToolchainRepo{DB: db}
-}
-
-func (s *TIPostgresRouteSuite) TearDownSuite() {
-	s.pool.Purge(s.resource)
-}
-
-func TestTIPostgresRouteSuite(t *testing.T) {
-	suite.Run(t, new(TIPostgresRouteSuite))
-}
diff --git a/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration_test.go b/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration_test.go
index 14db83817..aa6b3f8e9 100644
--- a/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration_test.go
+++ b/packages/operator/pkg/apiserver/routes/v1/training/toolchain_integration_test.go
@@ -19,22 +19,24 @@ package training_test
 import (
 	"bytes"
 	"encoding/json"
-	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
-	httputil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/httputil"
-	"net/http"
-	"net/http/httptest"
-	"strings"
-	"time"
-
 	"github.com/gin-gonic/gin"
 	"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/training"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes"
 	train_route "github.com/odahu/odahu-flow/packages/operator/pkg/apiserver/routes/v1/training"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/config"
 	odahuErrors "github.com/odahu/odahu-flow/packages/operator/pkg/errors"
-	mt_repository "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/service/toolchain"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
+	httputil "github.com/odahu/odahu-flow/packages/operator/pkg/utils/httputil"
 	. "github.com/onsi/gomega"
+	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/suite"
+	"net/http"
+	"net/http/httptest"
+	"strconv"
+	"strings"
+	"testing"
 )
 
 const (
@@ -50,28 +52,18 @@ var (
 
 type TIGenericRouteSuite struct {
 	suite.Suite
-	g            *GomegaWithT
-	server       *gin.Engine
-	mtRepository mt_repository.ToolchainRepository
+	g                    *GomegaWithT
+	server               *gin.Engine
+	toolchainServiceMock toolchain.MockToolchainService
 }
 
-func (s *TIGenericRouteSuite) TearDownTest() {
-	for _, mpID := range []string{
-		testToolchainIntegrationID,
-		testToolchainIntegrationID1,
-		testToolchainIntegrationID2,
-	} {
-		if err := s.mtRepository.DeleteToolchainIntegration(mpID); err != nil && !odahuErrors.IsNotFoundError(err) {
-			// If a model training is not found then it was not created during a test case
-			// All other errors propagate as a panic
-			panic(err)
-		}
-	}
+func TestTIGenericRouteSuite(t *testing.T) {
+	suite.Run(t, new(TIGenericRouteSuite))
 }
 
 func (s *TIGenericRouteSuite) SetupTest() {
 	s.g = NewGomegaWithT(s.T())
-
+	s.toolchainServiceMock = toolchain.MockToolchainService{}
 	s.registerHandlers(config.NewDefaultModelTrainingConfig())
 }
 
@@ -79,7 +71,7 @@ func (s *TIGenericRouteSuite) registerHandlers(trainingConfig config.ModelTraini
 	s.server = gin.Default()
 	v1Group := s.server.Group("")
 	trainingGroup := v1Group.Group("", routes.DisableAPIMiddleware(trainingConfig.Enabled))
-	train_route.ConfigureToolchainRoutes(trainingGroup, s.mtRepository)
+	train_route.ConfigureToolchainRoutes(trainingGroup, &s.toolchainServiceMock)
 }
 
 func newTiStub() *training.ToolchainIntegration {
@@ -93,27 +85,10 @@ func newTiStub() *training.ToolchainIntegration {
 	}
 }
 
-func (s *TIGenericRouteSuite) newMultipleTiStubs() {
-	ti1 := &training.ToolchainIntegration{
-		ID: testToolchainIntegrationID1,
-		Spec: v1alpha1.ToolchainIntegrationSpec{
-			DefaultImage: testToolchainMtImage,
-		},
-	}
-	s.g.Expect(s.mtRepository.CreateToolchainIntegration(ti1)).NotTo(HaveOccurred())
-
-	ti2 := &training.ToolchainIntegration{
-		ID: testToolchainIntegrationID2,
-		Spec: v1alpha1.ToolchainIntegrationSpec{
-			DefaultImage: testToolchainMtImage,
-		},
-	}
-	s.g.Expect(s.mtRepository.CreateToolchainIntegration(ti2)).NotTo(HaveOccurred())
-}
-
 func (s *TIGenericRouteSuite) TestGetToolchainIntegration() {
 	ti := newTiStub()
-	s.g.Expect(s.mtRepository.CreateToolchainIntegration(ti)).NotTo(HaveOccurred())
+
+	s.toolchainServiceMock.On("GetToolchainIntegration", ti.ID).Return(ti, nil)
 
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodGet, strings.Replace(
@@ -132,9 +107,15 @@ func (s *TIGenericRouteSuite) TestGetToolchainIntegration() {
 }
 
 func (s *TIGenericRouteSuite) TestGetToolchainIntegrationNotFound() {
+	toolchainID := "not-found"
+
+	s.toolchainServiceMock.
+		On("GetToolchainIntegration", toolchainID).
+		Return(nil, odahuErrors.NotFoundError{})
+
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodGet, strings.Replace(
-		train_route.GetToolchainIntegrationURL, ":id", "not-found", -1,
+		train_route.GetToolchainIntegrationURL, ":id", toolchainID, -1,
 	), nil)
 	s.g.Expect(err).NotTo(HaveOccurred())
 	s.server.ServeHTTP(w, req)
@@ -148,6 +129,10 @@ func (s *TIGenericRouteSuite) TestGetToolchainIntegrationNotFound() {
 }
 
 func (s *TIGenericRouteSuite) TestGetAllTiEmptyResult() {
+	s.toolchainServiceMock.
+		On("GetToolchainIntegrationList", mock.Anything, mock.Anything).
+		Return([]training.ToolchainIntegration{}, nil)
+
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodGet, train_route.GetAllToolchainIntegrationURL, nil)
 	s.g.Expect(err).NotTo(HaveOccurred())
@@ -162,7 +147,12 @@ func (s *TIGenericRouteSuite) TestGetAllTiEmptyResult() {
 }
 
 func (s *TIGenericRouteSuite) TestGetAllTi() {
-	s.newMultipleTiStubs()
+	s.toolchainServiceMock.
+		On("GetToolchainIntegrationList", mock.Anything, mock.Anything).
+		Return([]training.ToolchainIntegration{
+			{ID: testToolchainIntegrationID1},
+			{ID: testToolchainIntegrationID2},
+		}, nil)
 
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodGet, train_route.GetAllToolchainIntegrationURL, nil)
@@ -182,9 +172,13 @@ func (s *TIGenericRouteSuite) TestGetAllTi() {
 }
 
 func (s *TIGenericRouteSuite) TestGetAllTiPaging() {
-	s.newMultipleTiStubs()
-
-	toolchainsNames := map[string]interface{}{testToolchainIntegrationID1: nil, testToolchainIntegrationID2: nil}
+	expectedToolchains := []training.ToolchainIntegration{
+		{ID: testToolchainIntegrationID1},
+		{ID: testToolchainIntegrationID2},
+	}
+	s.toolchainServiceMock.
+		On("GetToolchainIntegrationList", mock.Anything, mock.Anything).
+		Return(expectedToolchains, nil)
 
 	// Return first page
 	w := httptest.NewRecorder()
@@ -192,66 +186,46 @@ func (s *TIGenericRouteSuite) TestGetAllTiPaging() {
 	s.g.Expect(err).NotTo(HaveOccurred())
 
 	query := req.URL.Query()
-	query.Set("size", "1")
-	query.Set("page", "0")
-	req.URL.RawQuery = query.Encode()
-
-	s.g.Expect(err).NotTo(HaveOccurred())
-	s.server.ServeHTTP(w, req)
-
-	var toolchains []training.ToolchainIntegration
-	err = json.Unmarshal(w.Body.Bytes(), &toolchains)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	s.g.Expect(w.Code).Should(Equal(http.StatusOK))
-	s.g.Expect(toolchains).Should(HaveLen(1))
-	delete(toolchainsNames, toolchains[0].ID)
 
-	// Return second page
-	w = httptest.NewRecorder()
-	req, err = http.NewRequest(http.MethodGet, train_route.GetAllToolchainIntegrationURL, nil)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	query = req.URL.Query()
-	query.Set("size", "1")
-	query.Set("page", "1")
+	expectedSize := 2
+	expectedPage := 0
+	query.Set("size", strconv.Itoa(expectedSize))
+	query.Set("page", strconv.Itoa(expectedPage))
 	req.URL.RawQuery = query.Encode()
 
 	s.g.Expect(err).NotTo(HaveOccurred())
 	s.server.ServeHTTP(w, req)
 
-	err = json.Unmarshal(w.Body.Bytes(), &toolchains)
-	s.g.Expect(err).NotTo(HaveOccurred())
+	// Check that query params properly converted to list options
+	sizeFunc := s.toolchainServiceMock.Calls[0].Arguments.Get(0).(filter.ListOption)
+	pageFunc := s.toolchainServiceMock.Calls[0].Arguments.Get(1).(filter.ListOption)
 
-	s.g.Expect(w.Code).Should(Equal(http.StatusOK))
-	s.g.Expect(toolchains).Should(HaveLen(1))
-	delete(toolchainsNames, toolchains[0].ID)
+	listOptions := &filter.ListOptions{}
+	sizeFunc(listOptions)
+	actualSize := *listOptions.Size
+	pageFunc(listOptions)
+	actualPage := *listOptions.Page
 
-	// Return third empty page
-	w = httptest.NewRecorder()
-	req, err = http.NewRequest(http.MethodGet, train_route.GetAllToolchainIntegrationURL, nil)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	query = req.URL.Query()
-	query.Set("size", "1")
-	query.Set("page", "2")
-	req.URL.RawQuery = query.Encode()
+	s.g.Expect(actualPage).To(Equal(expectedPage))
+	s.g.Expect(actualSize).To(Equal(expectedSize))
 
-	s.g.Expect(err).NotTo(HaveOccurred())
-	s.server.ServeHTTP(w, req)
-
-	err = json.Unmarshal(w.Body.Bytes(), &toolchains)
+	var actualToolchains []training.ToolchainIntegration
+	err = json.Unmarshal(w.Body.Bytes(), &actualToolchains)
 	s.g.Expect(err).NotTo(HaveOccurred())
 
 	s.g.Expect(w.Code).Should(Equal(http.StatusOK))
-	s.g.Expect(toolchains).Should(HaveLen(0))
-	s.g.Expect(toolchains).Should(BeEmpty())
+	s.g.Expect(actualToolchains).Should(HaveLen(2))
+	s.g.Expect(actualToolchains).To(Equal(expectedToolchains))
 }
 
 func (s *TIGenericRouteSuite) TestCreateToolchainIntegration() {
-	tiEntity := newTiStub()
+	expectedTI := newTiStub()
 
-	tiEntityBody, err := json.Marshal(tiEntity)
+	s.toolchainServiceMock.
+		On("CreateToolchainIntegration", expectedTI).
+		Return(nil)
+
+	tiEntityBody, err := json.Marshal(expectedTI)
 	s.g.Expect(err).NotTo(HaveOccurred())
 
 	w := httptest.NewRecorder()
@@ -266,40 +240,8 @@ func (s *TIGenericRouteSuite) TestCreateToolchainIntegration() {
 	s.g.Expect(err).NotTo(HaveOccurred())
 
 	s.g.Expect(w.Code).Should(Equal(http.StatusCreated))
-	s.g.Expect(tiResponse.ID).Should(Equal(tiEntity.ID))
-	s.g.Expect(tiResponse.Spec).Should(Equal(tiEntity.Spec))
-
-	ti, err := s.mtRepository.GetToolchainIntegration(testToolchainIntegrationID)
-	s.g.Expect(err).ShouldNot(HaveOccurred())
-	s.g.Expect(ti.Spec).To(Equal(tiEntity.Spec))
-}
-
-// CreatedAt and UpdatedAt field should automatically be updated after create request
-func (s *TIGenericRouteSuite) TestCreateToolchainIntegrationModifiable() {
-	newResource := newTiStub()
-
-	newResourceBody, err := json.Marshal(newResource)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	reqTime := routes.GetTimeNowTruncatedToSeconds()
-	w := httptest.NewRecorder()
-	req, err := http.NewRequest(
-		http.MethodPost, train_route.CreateToolchainIntegrationURL, bytes.NewReader(newResourceBody),
-	)
-	s.g.Expect(err).NotTo(HaveOccurred())
-	s.server.ServeHTTP(w, req)
-
-	var resp training.ToolchainIntegration
-	err = json.Unmarshal(w.Body.Bytes(), &resp)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	s.g.Expect(w.Code).Should(Equal(http.StatusCreated))
-	s.g.Expect(resp.Status.CreatedAt).NotTo(BeNil())
-	createdAtWasNotUpdated := reqTime.Before(resp.Status.CreatedAt) || reqTime.Equal(resp.Status.CreatedAt)
-	s.g.Expect(createdAtWasNotUpdated).Should(Equal(true))
-	s.g.Expect(resp.Status.UpdatedAt).NotTo(BeNil())
-	updatedAtWasUpdated := reqTime.Before(resp.Status.CreatedAt) || reqTime.Equal(resp.Status.CreatedAt)
-	s.g.Expect(updatedAtWasUpdated).Should(Equal(true))
+	s.g.Expect(tiResponse.ID).Should(Equal(expectedTI.ID))
+	s.g.Expect(tiResponse.Spec).Should(Equal(expectedTI.Spec))
 }
 
 func (s *TIGenericRouteSuite) TestCreateToolchainIntegrationValidation() {
@@ -327,7 +269,9 @@ func (s *TIGenericRouteSuite) TestCreateToolchainIntegrationValidation() {
 func (s *TIGenericRouteSuite) TestCreateDuplicateToolchainIntegration() {
 	ti := newTiStub()
 
-	s.g.Expect(s.mtRepository.CreateToolchainIntegration(ti)).NotTo(HaveOccurred())
+	s.toolchainServiceMock.
+		On("CreateToolchainIntegration", ti).
+		Return(odahuErrors.AlreadyExistError{})
 
 	tiEntityBody, err := json.Marshal(ti)
 	s.g.Expect(err).NotTo(HaveOccurred())
@@ -348,11 +292,10 @@ func (s *TIGenericRouteSuite) TestCreateDuplicateToolchainIntegration() {
 }
 
 func (s *TIGenericRouteSuite) TestUpdateToolchainIntegration() {
-	ti := newTiStub()
-	s.g.Expect(s.mtRepository.CreateToolchainIntegration(ti)).NotTo(HaveOccurred())
-
 	updatedTi := newTiStub()
-	updatedTi.Spec.Entrypoint = "new-entrypoint"
+	s.toolchainServiceMock.
+		On("UpdateToolchainIntegration", updatedTi).
+		Return(nil)
 
 	tiEntityBody, err := json.Marshal(updatedTi)
 	s.g.Expect(err).NotTo(HaveOccurred())
@@ -371,49 +314,9 @@ func (s *TIGenericRouteSuite) TestUpdateToolchainIntegration() {
 	s.g.Expect(w.Code).Should(Equal(http.StatusOK))
 	s.g.Expect(tiResponse.ID).Should(Equal(updatedTi.ID))
 	s.g.Expect(tiResponse.Spec).Should(Equal(updatedTi.Spec))
-
-	ti, err = s.mtRepository.GetToolchainIntegration(testToolchainIntegrationID)
-	s.g.Expect(err).NotTo(HaveOccurred())
-	s.g.Expect(ti.Spec).To(Equal(updatedTi.Spec))
-}
-
-// UpdatedAt field should automatically be updated after update request
-func (s *TIGenericRouteSuite) TestUpdateToolchainIntegrationModifiable() {
-	resource := newTiStub()
-	s.g.Expect(s.mtRepository.CreateToolchainIntegration(resource)).NotTo(HaveOccurred())
-
-	time.Sleep(1 * time.Second)
-
-	newResource := newTiStub()
-
-	newResourceBody, err := json.Marshal(newResource)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	reqTime := routes.GetTimeNowTruncatedToSeconds()
-	w := httptest.NewRecorder()
-	req, err := http.NewRequest(
-		http.MethodPut, train_route.UpdateToolchainIntegrationURL, bytes.NewReader(newResourceBody),
-	)
-	s.g.Expect(err).NotTo(HaveOccurred())
-	s.server.ServeHTTP(w, req)
-
-	var respResource training.ToolchainIntegration
-	err = json.Unmarshal(w.Body.Bytes(), &respResource)
-	s.g.Expect(err).NotTo(HaveOccurred())
-
-	s.g.Expect(w.Code).Should(Equal(http.StatusOK))
-	s.g.Expect(respResource.Status.CreatedAt).NotTo(BeNil())
-	createdAtWasNotUpdated := reqTime.After(respResource.Status.CreatedAt.Time)
-	s.g.Expect(createdAtWasNotUpdated).Should(Equal(true))
-	s.g.Expect(respResource.Status.UpdatedAt).NotTo(BeNil())
-	updatedAtWasUpdated := reqTime.Before(respResource.Status.UpdatedAt) || reqTime.Equal(respResource.Status.UpdatedAt)
-	s.g.Expect(updatedAtWasUpdated).Should(Equal(true))
 }
 
 func (s *TIGenericRouteSuite) TestUpdateToolchainIntegrationValidation() {
-	ti := newTiStub()
-	s.g.Expect(s.mtRepository.CreateToolchainIntegration(ti)).NotTo(HaveOccurred())
-
 	updatedTi := newTiStub()
 	updatedTi.Spec.Entrypoint = ""
 
@@ -438,6 +341,10 @@ func (s *TIGenericRouteSuite) TestUpdateToolchainIntegrationValidation() {
 func (s *TIGenericRouteSuite) TestUpdateToolchainIntegrationNotFound() {
 	ti := newTiStub()
 
+	s.toolchainServiceMock.
+		On("UpdateToolchainIntegration", ti).
+		Return(odahuErrors.NotFoundError{})
+
 	tiBody, err := json.Marshal(ti)
 	s.g.Expect(err).NotTo(HaveOccurred())
 
@@ -455,12 +362,14 @@ func (s *TIGenericRouteSuite) TestUpdateToolchainIntegrationNotFound() {
 }
 
 func (s *TIGenericRouteSuite) TestDeleteToolchainIntegration() {
-	ti := newTiStub()
-	s.g.Expect(s.mtRepository.CreateToolchainIntegration(ti)).NotTo(HaveOccurred())
+	id := "id"
+	s.toolchainServiceMock.
+		On("DeleteToolchainIntegration", id).
+		Return(nil)
 
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodDelete, strings.Replace(
-		train_route.DeleteToolchainIntegrationURL, ":id", ti.ID, -1,
+		train_route.DeleteToolchainIntegrationURL, ":id", id, -1,
 	), nil)
 	s.g.Expect(err).NotTo(HaveOccurred())
 	s.server.ServeHTTP(w, req)
@@ -471,16 +380,17 @@ func (s *TIGenericRouteSuite) TestDeleteToolchainIntegration() {
 
 	s.g.Expect(w.Code).Should(Equal(http.StatusOK))
 	s.g.Expect(result.Message).Should(ContainSubstring("was deleted"))
-
-	tiList, err := s.mtRepository.GetToolchainIntegrationList()
-	s.g.Expect(err).NotTo(HaveOccurred())
-	s.g.Expect(tiList).To(HaveLen(0))
 }
 
 func (s *TIGenericRouteSuite) TestDeleteToolchainIntegrationNotFound() {
+	notFoundID := "not-found"
+	s.toolchainServiceMock.
+		On("DeleteToolchainIntegration", notFoundID).
+		Return(odahuErrors.NotFoundError{})
+
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodDelete, strings.Replace(
-		train_route.DeleteToolchainIntegrationURL, ":id", "not-found", -1,
+		train_route.DeleteToolchainIntegrationURL, ":id", notFoundID, -1,
 	), nil)
 	s.g.Expect(err).NotTo(HaveOccurred())
 	s.server.ServeHTTP(w, req)
@@ -561,12 +471,19 @@ func (s *TIGenericRouteSuite) TestDisabledAPICreateToolchainIntegrationValidatio
 	s.g.Expect(result.Message).Should(ContainSubstring(routes.DisabledAPIErrorMessage))
 }
 
+// GET requests are served despite API is disabled
 func (s *TIGenericRouteSuite) TestDisabledAPIGetAllTi() {
 	trainingConfig := config.NewDefaultModelTrainingConfig()
 	trainingConfig.Enabled = false
 	s.registerHandlers(trainingConfig)
 
-	s.newMultipleTiStubs()
+	expectedToolchains := []training.ToolchainIntegration{
+		{ID: testToolchainIntegrationID1},
+		{ID: testToolchainIntegrationID2},
+	}
+	s.toolchainServiceMock.
+		On("GetToolchainIntegrationList", mock.Anything, mock.Anything).
+		Return(expectedToolchains, nil)
 
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodGet, train_route.GetAllToolchainIntegrationURL, nil)
@@ -591,7 +508,9 @@ func (s *TIGenericRouteSuite) TestDisabledAPIGetToolchainIntegration() {
 	s.registerHandlers(trainingConfig)
 
 	ti := newTiStub()
-	s.g.Expect(s.mtRepository.CreateToolchainIntegration(ti)).NotTo(HaveOccurred())
+	s.toolchainServiceMock.
+		On("GetToolchainIntegration", ti.ID).
+		Return(ti, nil)
 
 	w := httptest.NewRecorder()
 	req, err := http.NewRequest(http.MethodGet, strings.Replace(
diff --git a/packages/operator/pkg/repository/connection/kubernetes/connection_test.go b/packages/operator/pkg/repository/connection/kubernetes/connection_test.go
index 0a5ab64ea..ce3713074 100644
--- a/packages/operator/pkg/repository/connection/kubernetes/connection_test.go
+++ b/packages/operator/pkg/repository/connection/kubernetes/connection_test.go
@@ -40,7 +40,7 @@ func TestConnectionRepository(t *testing.T) {
 		},
 	}
 
-	err := c.CreateConnection(created)
+	err := c.SaveConnection(created)
 	g.Expect(err).NotTo(HaveOccurred())
 
 	fetched, err := c.GetConnection(connID)
diff --git a/packages/operator/pkg/repository/connection/vault/connection_test.go b/packages/operator/pkg/repository/connection/vault/connection_test.go
index 7d60c7550..e0d89f29a 100644
--- a/packages/operator/pkg/repository/connection/vault/connection_test.go
+++ b/packages/operator/pkg/repository/connection/vault/connection_test.go
@@ -99,7 +99,7 @@ func (s *VaultConnRepoSuite) TestConnectionRepository() {
 		},
 	}
 
-	err := s.connRepo.CreateConnection(created)
+	err := s.connRepo.SaveConnection(created)
 	s.g.Expect(err).NotTo(HaveOccurred())
 
 	fetched, err := s.connRepo.GetConnection(connID)
diff --git a/packages/operator/pkg/repository/deployment/postgres/model_deployment_test.go b/packages/operator/pkg/repository/deployment/postgres/model_deployment_test.go
index ef0b71876..7ed86bcfb 100644
--- a/packages/operator/pkg/repository/deployment/postgres/model_deployment_test.go
+++ b/packages/operator/pkg/repository/deployment/postgres/model_deployment_test.go
@@ -22,7 +22,7 @@ func TestModelDeploymentRepository(t *testing.T) {
 
 	as := assert.New(t)
 	created := &deployment.ModelDeployment{
-		ID: mdID,
+		ID:        mdID,
 		CreatedAt: time.Now().Round(time.Microsecond),
 		UpdatedAt: time.Now().Round(time.Microsecond),
 		Spec: v1alpha1.ModelDeploymentSpec{
@@ -30,9 +30,9 @@ func TestModelDeploymentRepository(t *testing.T) {
 		},
 	}
 
-	as.NoError(repo.CreateModelDeployment(context.Background(), nil, created))
+	as.NoError(repo.SaveModelDeployment(context.Background(), nil, created))
 
-	as.Exactly(repo.CreateModelDeployment(context.Background(), nil, created), odahuErrors.AlreadyExistError{
+	as.Exactly(repo.SaveModelDeployment(context.Background(), nil, created), odahuErrors.AlreadyExistError{
 		Entity: mdID,
 	})
 
@@ -69,13 +69,12 @@ func TestModelDeploymentRepository(t *testing.T) {
 	as.NoError(err)
 	as.Len(tis, 1)
 
-
 	as.False(fetched.DeletionMark)
 	as.NoError(repo.SetDeletionMark(context.Background(), nil, mdID, true))
 	fetched, err = repo.GetModelDeployment(context.Background(), nil, mdID)
 	as.NoError(err)
 	as.True(fetched.DeletionMark)
-	
+
 	as.NoError(repo.DeleteModelDeployment(context.Background(), nil, mdID))
 	_, err = repo.GetModelDeployment(context.Background(), nil, mdID)
 
diff --git a/packages/operator/pkg/repository/packaging/postgres/model_packaging_test.go b/packages/operator/pkg/repository/packaging/postgres/model_packaging_test.go
index 9607dcea9..327903f65 100644
--- a/packages/operator/pkg/repository/packaging/postgres/model_packaging_test.go
+++ b/packages/operator/pkg/repository/packaging/postgres/model_packaging_test.go
@@ -53,12 +53,12 @@ var (
 
 type MPRepositorySuite struct {
 	suite.Suite
-	rep       mp_repository.Repository
+	rep mp_repository.Repository
 }
 
 func generateMP() *packaging.ModelPackaging {
 	return &packaging.ModelPackaging{
-		ID: mpID,
+		ID:        mpID,
 		CreatedAt: time.Now().Round(time.Microsecond),
 		UpdatedAt: time.Now().Round(time.Microsecond),
 		Spec: packaging.ModelPackagingSpec{
@@ -93,7 +93,7 @@ func (s *MPRepositorySuite) TestModelPackagingRepository() {
 
 	ctx := context.Background()
 
-	assert.NoError(s.T(), s.rep.CreateModelPackaging(ctx, nil, created))
+	assert.NoError(s.T(), s.rep.SaveModelPackaging(ctx, nil, created))
 
 	fetched, err := s.rep.GetModelPackaging(ctx, nil, mpID)
 	assert.NoError(s.T(), err)
@@ -116,20 +116,18 @@ func (s *MPRepositorySuite) TestModelPackagingRepository() {
 
 	newStatus := odahuflowv1alpha1.ModelPackagingStatus{PodName: "Some name"}
 	assert.NoError(s.T(), s.rep.UpdateModelPackagingStatus(ctx, nil, mpID, newStatus))
-	fetched, err = s.rep.GetModelPackaging(ctx, nil,  mpID)
+	fetched, err = s.rep.GetModelPackaging(ctx, nil, mpID)
 	assert.NoError(s.T(), err)
 	assert.Exactly(s.T(), fetched.Status.PodName, "Some name")
 
 	assert.False(s.T(), fetched.DeletionMark)
-	assert.NoError(s.T(), s.rep.SetDeletionMark(ctx, nil,  mpID, true))
-	fetched, err = s.rep.GetModelPackaging(ctx, nil,  mpID)
+	assert.NoError(s.T(), s.rep.SetDeletionMark(ctx, nil, mpID, true))
+	fetched, err = s.rep.GetModelPackaging(ctx, nil, mpID)
 	assert.NoError(s.T(), err)
 	assert.True(s.T(), fetched.DeletionMark)
 
-
-	assert.NoError(s.T(), s.rep.DeleteModelPackaging(ctx, nil,  mpID))
-	_, err = s.rep.GetModelPackaging(ctx, nil,  mpID)
+	assert.NoError(s.T(), s.rep.DeleteModelPackaging(ctx, nil, mpID))
+	_, err = s.rep.GetModelPackaging(ctx, nil, mpID)
 	assert.Error(s.T(), err)
 	assert.Exactly(s.T(), err, odahuErrors.NotFoundError{Entity: mpID})
 }
-
diff --git a/packages/operator/pkg/repository/packaging/postgres/packaging_integration_test.go b/packages/operator/pkg/repository/packaging/postgres/packaging_integration_test.go
index 66557ea96..f4880992f 100644
--- a/packages/operator/pkg/repository/packaging/postgres/packaging_integration_test.go
+++ b/packages/operator/pkg/repository/packaging/postgres/packaging_integration_test.go
@@ -91,7 +91,7 @@ func TestPackagingRepository(t *testing.T) {
 		},
 	}
 
-	g.Expect(rep.CreatePackagingIntegration(created)).NotTo(HaveOccurred())
+	g.Expect(rep.SavePackagingIntegration(created)).NotTo(HaveOccurred())
 
 	fetched, err := rep.GetPackagingIntegration(piID)
 	g.Expect(err).NotTo(HaveOccurred())
diff --git a/packages/operator/pkg/repository/route/postgres/model_route_test.go b/packages/operator/pkg/repository/route/postgres/model_route_test.go
index 664aa2458..b37237efd 100644
--- a/packages/operator/pkg/repository/route/postgres/model_route_test.go
+++ b/packages/operator/pkg/repository/route/postgres/model_route_test.go
@@ -22,7 +22,7 @@ func TestModelRouteRepository(t *testing.T) {
 
 	as := assert.New(t)
 	created := &deployment.ModelRoute{
-		ID: mrID,
+		ID:        mrID,
 		CreatedAt: time.Now().Round(time.Microsecond),
 		UpdatedAt: time.Now().Round(time.Microsecond),
 		Spec: v1alpha1.ModelRouteSpec{
@@ -30,9 +30,9 @@ func TestModelRouteRepository(t *testing.T) {
 		},
 	}
 
-	as.NoError(repo.CreateModelRoute(context.Background(), nil, created))
+	as.NoError(repo.SaveModelRoute(context.Background(), nil, created))
 
-	as.Exactly(repo.CreateModelRoute(context.Background(), nil, created), odahuErrors.AlreadyExistError{Entity: mrID})
+	as.Exactly(repo.SaveModelRoute(context.Background(), nil, created), odahuErrors.AlreadyExistError{Entity: mrID})
 
 	fetched, err := repo.GetModelRoute(context.Background(), nil, mrID)
 	as.NoError(err)
@@ -67,7 +67,6 @@ func TestModelRouteRepository(t *testing.T) {
 	as.NoError(err)
 	as.Len(tis, 1)
 
-
 	as.False(fetched.DeletionMark)
 	as.NoError(repo.SetDeletionMark(context.Background(), nil, mrID, true))
 	fetched, err = repo.GetModelRoute(context.Background(), nil, mrID)
@@ -79,4 +78,4 @@ func TestModelRouteRepository(t *testing.T) {
 
 	as.Error(err)
 	as.Exactly(err, odahuErrors.NotFoundError{Entity: mrID})
-}
\ No newline at end of file
+}
diff --git a/packages/operator/pkg/repository/training/postgres/model_training_test.go b/packages/operator/pkg/repository/training/postgres/model_training_test.go
index e064d11b4..821504f63 100644
--- a/packages/operator/pkg/repository/training/postgres/model_training_test.go
+++ b/packages/operator/pkg/repository/training/postgres/model_training_test.go
@@ -28,7 +28,7 @@ func (s *Suite) SetupSuite() {
 
 func (s *Suite) TearDownTest() {
 	err := s.repo.DeleteModelTraining(context.Background(), nil, mtID)
-	if err != nil  && !odahuErrors.IsNotFoundError(err) {
+	if err != nil && !odahuErrors.IsNotFoundError(err) {
 		s.T().Fatal(err)
 	}
 }
@@ -44,7 +44,7 @@ func (s *Suite) TestModelTrainingRepository() {
 	g := NewGomegaWithT(s.T())
 
 	created := &training.ModelTraining{
-		ID: mtID,
+		ID:        mtID,
 		CreatedAt: time.Now().Round(time.Microsecond),
 		UpdatedAt: time.Now().Round(time.Microsecond),
 		Spec: v1alpha1.ModelTrainingSpec{
@@ -52,9 +52,9 @@ func (s *Suite) TestModelTrainingRepository() {
 		},
 	}
 
-	g.Expect(tRepo.CreateModelTraining(context.TODO(), nil, created)).NotTo(HaveOccurred())
+	g.Expect(tRepo.SaveModelTraining(context.TODO(), nil, created)).NotTo(HaveOccurred())
 
-	g.Expect(tRepo.CreateModelTraining(context.TODO(), nil, created)).To(And(
+	g.Expect(tRepo.SaveModelTraining(context.TODO(), nil, created)).To(And(
 		HaveOccurred(),
 		MatchError(odahuErrors.AlreadyExistError{Entity: mtID}),
 	))
@@ -67,7 +67,7 @@ func (s *Suite) TestModelTrainingRepository() {
 	g.Expect(fetched.UpdatedAt.Equal(created.UpdatedAt)).Should(BeTrue())
 
 	updated := &training.ModelTraining{
-		ID: mtID,
+		ID:        mtID,
 		UpdatedAt: created.CreatedAt.Add(time.Hour),
 		Spec: v1alpha1.ModelTrainingSpec{
 			WorkDir: "/foo-updated",
diff --git a/packages/operator/pkg/repository/training/postgres/toolchain_integration_test.go b/packages/operator/pkg/repository/training/postgres/toolchain_integration_test.go
index b2827ce1f..c56a15018 100644
--- a/packages/operator/pkg/repository/training/postgres/toolchain_integration_test.go
+++ b/packages/operator/pkg/repository/training/postgres/toolchain_integration_test.go
@@ -29,9 +29,9 @@ func TestToolchainRepository(t *testing.T) {
 		},
 	}
 
-	g.Expect(tRepo.CreateToolchainIntegration(created)).NotTo(HaveOccurred())
+	g.Expect(tRepo.SaveToolchainIntegration(created)).NotTo(HaveOccurred())
 
-	g.Expect(tRepo.CreateToolchainIntegration(created)).To(And(
+	g.Expect(tRepo.SaveToolchainIntegration(created)).To(And(
 		HaveOccurred(),
 		MatchError(odahuErrors.AlreadyExistError{Entity: tiID}),
 	))
diff --git a/packages/operator/pkg/service/connection/connection_service_test.go b/packages/operator/pkg/service/connection/connection_service_test.go
index 0643e90ae..a6a9f416c 100644
--- a/packages/operator/pkg/service/connection/connection_service_test.go
+++ b/packages/operator/pkg/service/connection/connection_service_test.go
@@ -28,6 +28,7 @@ import (
 	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/suite"
 	"testing"
+	"time"
 )
 
 const connID = "some-id"
@@ -142,6 +143,8 @@ func (s *ConnectionServiceTestSuite) TestDeleteConnection() {
 
 func (s *ConnectionServiceTestSuite) TestUpdateConnection() {
 	originalConnection := stubConnection()
+	originalConnection.CreatedAt = time.Now()
+	originalConnection.UpdatedAt = originalConnection.CreatedAt
 	connectionForService := originalConnection
 
 	connectionForService.EncodeBase64Fields()
@@ -155,7 +158,10 @@ func (s *ConnectionServiceTestSuite) TestUpdateConnection() {
 	assert.Nil(s.T(), err)
 
 	// Check that connection was base64-decoded before sending to repo
-	assert.Equal(s.T(), originalConnection, s.mockRepo.UpdatedConnection)
+	assert.Equal(s.T(), originalConnection.Spec, s.mockRepo.UpdatedConnection.Spec)
+
+	s.Assert().Equal(originalConnection.CreatedAt, updatedConnection.CreatedAt)
+	s.Assert().True(originalConnection.UpdatedAt.Before(updatedConnection.UpdatedAt))
 
 	// Check that secrets are masked in result
 	assert.Equal(s.T(), connection.DecryptedDataMask, updatedConnection.Spec.KeyID)
@@ -202,23 +208,28 @@ func (s *ConnectionServiceTestSuite) TestCreateConnection() {
 	connectionForService.EncodeBase64Fields()
 
 	s.mockRepo.
-		On("CreateConnection", mock.AnythingOfType("*connection.Connection")).
+		On("SaveConnection", mock.AnythingOfType("*connection.Connection")).
 		Return(nil)
 
-	updatedConnection, err := s.connectionService.CreateConnection(connectionForService)
+	createdConnection, err := s.connectionService.CreateConnection(connectionForService)
 
-	assert.Nil(s.T(), err)
+	s.Assert().Nil(err)
 
 	// Check that connection was base64-decoded before sending to repo
-	assert.Equal(s.T(), originalConnection, s.mockRepo.CreatedConnection)
+	s.Assert().Equal(originalConnection.Spec, s.mockRepo.CreatedConnection.Spec)
+
+	// Check CreatedAt/UpdatedAt handled correctly
+	s.Assert().NotNil(createdConnection.CreatedAt)
+	s.Assert().Equal(createdConnection.UpdatedAt, createdConnection.CreatedAt)
+	s.Assert().True(createdConnection.CreatedAt.Before(time.Now()))
 
 	// Check that secrets are masked in result
-	assert.Equal(s.T(), connection.DecryptedDataMask, updatedConnection.Spec.KeyID)
-	assert.Equal(s.T(), connection.DecryptedDataMask, updatedConnection.Spec.KeySecret)
-	assert.Equal(s.T(), connection.DecryptedDataMask, updatedConnection.Spec.Password)
+	s.Assert().Equal(connection.DecryptedDataMask, createdConnection.Spec.KeyID)
+	s.Assert().Equal(connection.DecryptedDataMask, createdConnection.Spec.KeySecret)
+	s.Assert().Equal(connection.DecryptedDataMask, createdConnection.Spec.Password)
 	// Check that public key is base64-encoded
 	expectedPublicKey := base64.StdEncoding.EncodeToString([]byte(originalConnection.Spec.PublicKey))
-	assert.Equal(s.T(), expectedPublicKey, updatedConnection.Spec.PublicKey)
+	s.Assert().Equal(expectedPublicKey, createdConnection.Spec.PublicKey)
 }
 
 func (s *ConnectionServiceTestSuite) TestCreateConnection_DecodingFail() {
@@ -241,7 +252,7 @@ func (s *ConnectionServiceTestSuite) TestCreateConnection_ErrorFromRepo() {
 	connectionForService.EncodeBase64Fields()
 
 	s.mockRepo.
-		On("CreateConnection", mock.AnythingOfType("*connection.Connection")).
+		On("SaveConnection", mock.AnythingOfType("*connection.Connection")).
 		Return(errors.New("error from repo"))
 
 	createdConnection, err := s.connectionService.CreateConnection(connectionForService)
diff --git a/packages/operator/pkg/service/deployment/deployment_service_test.go b/packages/operator/pkg/service/deployment/deployment_service_test.go
index 5cf35ce02..23503056d 100644
--- a/packages/operator/pkg/service/deployment/deployment_service_test.go
+++ b/packages/operator/pkg/service/deployment/deployment_service_test.go
@@ -26,8 +26,8 @@ import (
 	odahu_errs "github.com/odahu/odahu-flow/packages/operator/pkg/errors"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/repository/deployment/mocks"
 	route_mocks "github.com/odahu/odahu-flow/packages/operator/pkg/repository/route/mocks"
-	event_pub_mocks "github.com/odahu/odahu-flow/packages/operator/pkg/service/deployment/mocks"
 	service "github.com/odahu/odahu-flow/packages/operator/pkg/service/deployment"
+	event_pub_mocks "github.com/odahu/odahu-flow/packages/operator/pkg/service/deployment/mocks"
 	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
@@ -46,14 +46,14 @@ func TestSuiteRun(t *testing.T) {
 
 type TestSuite struct {
 	suite.Suite
-	mockRepo *mocks.Repository
+	mockRepo  *mocks.Repository
 	rMockRepo *route_mocks.Repository
-	eMockPub *event_pub_mocks.EventPublisher
-	service service.Service
-	db *sql.DB
-	dbMock sqlmock.Sqlmock
-	as *assert.Assertions
-	nilTx *sql.Tx
+	eMockPub  *event_pub_mocks.EventPublisher
+	service   service.Service
+	db        *sql.DB
+	dbMock    sqlmock.Sqlmock
+	as        *assert.Assertions
+	nilTx     *sql.Tx
 }
 
 func (s *TestSuite) SetupSuite() {
@@ -128,20 +128,18 @@ func (s *TestSuite) TestDeleteModelDeployment() {
 
 	ctx := context.Background()
 
-
 	// Assume transaction commit
 	s.dbMock.ExpectBegin()
 	s.dbMock.ExpectCommit()
 	mockTx, err := s.db.Begin()
 	as.NoError(err)
 
-
 	defaultRouteID := "defaultRoute"
 	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
 	s.rMockRepo.On("GetModelRouteList", ctx, mockTx, mock.Anything).Return([]apis.ModelRoute{
 		{
-			ID:           defaultRouteID,
-			Default:      true,
+			ID:      defaultRouteID,
+			Default: true,
 		},
 	}, nil)
 	s.rMockRepo.On("DeleteModelRoute", ctx, mockTx, defaultRouteID).Return(nil)
@@ -188,29 +186,11 @@ func (s *TestSuite) TestUpdateModelDeployment() {
 	s.mockRepo.On("UpdateModelDeployment", ctx, mockTx, en).Return(nil)
 	s.eMockPub.On("PublishEvent", ctx, mockTx, mock.Anything).Return(nil)
 
+	timeBeforeCall := time.Now()
 	as.NoError(s.service.UpdateModelDeployment(ctx, en))
 	s.mockRepo.AssertExpectations(s.T())
 	as.NoError(s.dbMock.ExpectationsWereMet())
-}
-
-func (s *TestSuite) TestUpdateModelDeployment_UpdatedAt() {
-	as := assert.New(s.T())
-
-	ctx := context.Background()
-
-	// Assume transaction commit
-	s.dbMock.ExpectBegin()
-	s.dbMock.ExpectCommit()
-	mockTx, err := s.db.Begin()
-	as.NoError(err)
-
-	en := newStubMT()
-	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
-	s.mockRepo.On("UpdateModelDeployment", ctx, mockTx, en).Return(nil)
-	s.eMockPub.On("PublishEvent", ctx, mockTx, mock.Anything).Return(nil)
 
-	timeBeforeCall := time.Now()
-	as.NoError(s.service.UpdateModelDeployment(ctx, en))
 	// UpdatedAt field must be updated on now during the invocation
 	as.True(timeBeforeCall.Before(en.UpdatedAt))
 	// UpdatedAt field must be not updated on now during the invocation
@@ -270,7 +250,6 @@ func (s *TestSuite) TestUpdateModelDeploymentStatusSpecTouched() {
 	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
 	s.eMockPub.On("PublishEvent", ctx, mockTx, mock.Anything).Return(nil)
 
-
 	// Assume that repository return no error while set new status with not touched spec snapshot
 	newStatus := repoEn.Status
 	newStatus.Replicas = 3
@@ -297,7 +276,6 @@ func (s *TestSuite) TestCreateModelDeployment() {
 	en := newStubMT()
 	ctx := context.Background()
 
-
 	// Assume transaction commit
 	s.dbMock.ExpectBegin()
 	s.dbMock.ExpectCommit()
@@ -306,47 +284,21 @@ func (s *TestSuite) TestCreateModelDeployment() {
 		s.T().Fatal(err)
 	}
 	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
-	s.mockRepo.On("CreateModelDeployment", ctx, mockTx, en).Return(nil)
+	s.mockRepo.On("SaveModelDeployment", ctx, mockTx, en).Return(nil)
 	s.rMockRepo.On("DefaultExists", ctx, enID, mockTx).Return(false, nil)
-	s.rMockRepo.On("CreateModelRoute", ctx, mockTx, mock.Anything).
+	s.rMockRepo.On("SaveModelRoute", ctx, mockTx, mock.Anything).
 		Return(nil)
 	s.eMockPub.On("PublishEvent", ctx, mockTx, mock.Anything).Return(nil)
 
+	timeBeforeCall := time.Now()
+
 	as.NoError(s.service.CreateModelDeployment(ctx, en))
 	s.mockRepo.AssertExpectations(s.T())
 	as.NoError(s.dbMock.ExpectationsWereMet())
 
-}
-
-func (s *TestSuite) TestCreateModelDeployment_CreatedAt() {
-	as := assert.New(s.T())
-
-	en := newStubMT()
-	ctx := context.Background()
-
-
-	// Assume transaction commit
-	s.dbMock.ExpectBegin()
-	s.dbMock.ExpectCommit()
-	mockTx, err := s.db.Begin()
-	if err != nil {
-		s.T().Fatal(err)
-	}
-	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
-	s.mockRepo.On("CreateModelDeployment", ctx, mockTx, en).Return(nil)
-	s.rMockRepo.On("DefaultExists", ctx, enID, mockTx).Return(false, nil)
-	s.rMockRepo.On("CreateModelRoute", ctx, mockTx, mock.Anything).
-		Return(nil)
-	s.eMockPub.On("PublishEvent", ctx, mockTx, mock.Anything).Return(nil)
-
-	timeBeforeCall := time.Now()
-	as.NoError(s.service.CreateModelDeployment(ctx, en))
 	// CreatedAt, UpdatedAt fields must be updated on now during the invocation
 	as.True(timeBeforeCall.Before(en.CreatedAt))
 	as.True(timeBeforeCall.Before(en.UpdatedAt))
-	s.mockRepo.AssertExpectations(s.T())
-	as.NoError(s.dbMock.ExpectationsWereMet())
-
 }
 
 func (s *TestSuite) TestCreateModelDeployment_Error() {
@@ -355,8 +307,7 @@ func (s *TestSuite) TestCreateModelDeployment_Error() {
 	en := newStubMT()
 
 	ctx := context.Background()
-	anyError  := errors.New("any error")
-
+	anyError := errors.New("any error")
 
 	// Assume transaction commit
 	s.dbMock.ExpectBegin()
@@ -366,12 +317,12 @@ func (s *TestSuite) TestCreateModelDeployment_Error() {
 		s.T().Fatal(err)
 	}
 	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
-	s.mockRepo.On("CreateModelDeployment", ctx, mockTx, en).Return(anyError)
+	s.mockRepo.On("SaveModelDeployment", ctx, mockTx, en).Return(anyError)
 
 	as.Error(s.service.CreateModelDeployment(ctx, en))
 	s.mockRepo.AssertExpectations(s.T())
 	s.rMockRepo.AssertNotCalled(s.T(), "DefaultExists")
-	s.rMockRepo.AssertNotCalled(s.T(), "CreateModelRoute")
+	s.rMockRepo.AssertNotCalled(s.T(), "SaveModelRoute")
 	as.NoError(s.dbMock.ExpectationsWereMet())
 }
 
@@ -389,4 +340,4 @@ func newStubMT() *apis.ModelDeployment {
 		Spec:         v1alpha1.ModelDeploymentSpec{},
 		Status:       v1alpha1.ModelDeploymentStatus{},
 	}
-}
\ No newline at end of file
+}
diff --git a/packages/operator/pkg/service/packaging/packaging_service_test.go b/packages/operator/pkg/service/packaging/packaging_service_test.go
index d2825fe51..20fe7f467 100644
--- a/packages/operator/pkg/service/packaging/packaging_service_test.go
+++ b/packages/operator/pkg/service/packaging/packaging_service_test.go
@@ -45,14 +45,13 @@ func TestSuiteRun(t *testing.T) {
 type TestSuite struct {
 	suite.Suite
 	mockRepo *mocks.Repository
-	service service.Service
-	db *sql.DB
-	dbMock sqlmock.Sqlmock
-	as *assert.Assertions
-	nilTx *sql.Tx
+	service  service.Service
+	db       *sql.DB
+	dbMock   sqlmock.Sqlmock
+	as       *assert.Assertions
+	nilTx    *sql.Tx
 }
 
-
 func (s *TestSuite) SetupSuite() {
 	s.as = assert.New(s.T())
 }
@@ -180,7 +179,6 @@ func (s *TestSuite) TestUpdateModelPackagingStatus() {
 	s.mockRepo.On("GetModelPackaging", ctx, mockTx, enID).Return(repoEn, nil)
 	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
 
-
 	// Assume that repository return no error while set new status with not touched spec snapshot
 	newStatus := repoEn.Status
 	newStatus.PodName = "new Pod name"
@@ -237,7 +235,7 @@ func (s *TestSuite) TestCreateModelPackaging() {
 
 	en := newStubMT()
 	ctx := context.Background()
-	s.mockRepo.On("CreateModelPackaging", ctx, s.nilTx, en).Return(nil)
+	s.mockRepo.On("SaveModelPackaging", ctx, s.nilTx, en).Return(nil)
 
 	as.NoError(s.service.CreateModelPackaging(ctx, en))
 	s.mockRepo.AssertExpectations(s.T())
@@ -247,7 +245,7 @@ func (s *TestSuite) TestCreateModelPackaging_CreatedAt() {
 
 	en := newStubMT()
 	ctx := context.Background()
-	s.mockRepo.On("CreateModelPackaging", ctx, s.nilTx, en).Return(nil)
+	s.mockRepo.On("SaveModelPackaging", ctx, s.nilTx, en).Return(nil)
 
 	timeBeforeCall := time.Now()
 	as.NoError(s.service.CreateModelPackaging(ctx, en))
@@ -263,7 +261,7 @@ func (s *TestSuite) TestCreateModelPackaging_Error() {
 	en := newStubMT()
 	ctx := context.Background()
 	anyError := errors.New("any error")
-	s.mockRepo.On("CreateModelPackaging", ctx, s.nilTx, en).Return(anyError)
+	s.mockRepo.On("SaveModelPackaging", ctx, s.nilTx, en).Return(anyError)
 
 	as.Error(s.service.CreateModelPackaging(ctx, en))
 	s.mockRepo.AssertExpectations(s.T())
@@ -283,4 +281,4 @@ func newStubMT() *apis.ModelPackaging {
 		Spec:         apis.ModelPackagingSpec{},
 		Status:       v1alpha1.ModelPackagingStatus{},
 	}
-}
\ No newline at end of file
+}
diff --git a/packages/operator/pkg/service/packaging_integration/packaging_integration_service_test.go b/packages/operator/pkg/service/packaging_integration/packaging_integration_service_test.go
new file mode 100644
index 000000000..b26da7576
--- /dev/null
+++ b/packages/operator/pkg/service/packaging_integration/packaging_integration_service_test.go
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2021 EPAM Systems
+ *
+ * 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 packaging_integration //nolint
+
+import (
+	"errors"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/apis/packaging"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/repository/packaging/mocks"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/suite"
+	"testing"
+	"time"
+)
+
+func TestSuiteRun(t *testing.T) {
+	suite.Run(t, new(TestSuite))
+}
+
+type TestSuite struct {
+	suite.Suite
+	piService  *PackagingIntegrationService
+	piRepoMock *mocks.PackagingIntegrationRepository
+}
+
+func (s *TestSuite) SetupTest() {
+	s.piRepoMock = &mocks.PackagingIntegrationRepository{}
+	s.piService = NewService(s.piRepoMock)
+}
+
+// Tests that GetPackagingIntegration method proxies to repo
+func (s *TestSuite) TestGetPackagingIntegration() {
+	id := "some-id"
+
+	expectedPI := &packaging.PackagingIntegration{}
+	expectedErr := errors.New("some-error")
+
+	s.piRepoMock.On("GetPackagingIntegration", id).Return(expectedPI, expectedErr)
+
+	pi, err := s.piService.GetPackagingIntegration(id)
+
+	s.Assert().Equal(pi, expectedPI)
+	s.Assert().Equal(err, expectedErr)
+}
+
+// Tests that GetPackagingIntegrationList method proxies to repo
+func (s *TestSuite) TestGetPackagingIntegrationList() {
+	listOption := filter.Page(1)
+
+	expectedPIList := []packaging.PackagingIntegration{}
+	expectedErr := errors.New("some-error")
+
+	s.piRepoMock.
+		On("GetPackagingIntegrationList", mock.AnythingOfType("filter.ListOption")).
+		Return(expectedPIList, expectedErr)
+
+	piList, err := s.piService.GetPackagingIntegrationList(listOption)
+
+	s.Assert().Equal(piList, expectedPIList)
+	s.Assert().Equal(err, expectedErr)
+}
+
+// Tests that CreatePackagingIntegration method sets CreatedAt/UpdatedAt and proxies to repo
+func (s *TestSuite) TestCreatePackagingIntegration() {
+	somePI := &packaging.PackagingIntegration{ID: "some-pi"}
+
+	expectedErr := errors.New("some-error")
+
+	s.piRepoMock.
+		On("SavePackagingIntegration", somePI).
+		Return(expectedErr)
+
+	timeBefore := time.Now()
+	err := s.piService.CreatePackagingIntegration(somePI)
+
+	actualPI := s.piRepoMock.Calls[0].Arguments.Get(0).(*packaging.PackagingIntegration)
+
+	s.Assert().Equal(somePI, actualPI)
+	s.Assert().Equal(err, expectedErr)
+
+	s.Assert().True(somePI.CreatedAt.After(timeBefore))
+	s.Assert().Equal(somePI.CreatedAt, somePI.UpdatedAt)
+}
+
+// Tests that CreatePackagingIntegration method sets CreatedAt/UpdatedAt and proxies to repo
+func (s *TestSuite) TestUpdatePackagingIntegration() {
+	somePI := &packaging.PackagingIntegration{ID: "some-pi", CreatedAt: time.Now(), UpdatedAt: time.Now()}
+
+	expectedErr := errors.New("some-error")
+
+	s.piRepoMock.
+		On("UpdatePackagingIntegration", somePI).
+		Return(expectedErr)
+
+	timeBeforeUpdate := time.Now()
+	err := s.piService.UpdatePackagingIntegration(somePI)
+
+	actualPI := s.piRepoMock.Calls[0].Arguments.Get(0).(*packaging.PackagingIntegration)
+
+	s.Assert().Equal(somePI, actualPI)
+	s.Assert().Equal(err, expectedErr)
+
+	s.Assert().True(somePI.UpdatedAt.After(timeBeforeUpdate))
+	s.Assert().True(somePI.CreatedAt.Before(timeBeforeUpdate))
+}
+
+// Tests that DeletePackagingIntegration method proxies to repo
+func (s *TestSuite) TestDeletePackagingIntegration() {
+	id := "some-id"
+	expectedErr := errors.New("some-error")
+
+	s.piRepoMock.On("DeletePackagingIntegration", id).Return(expectedErr)
+
+	err := s.piService.DeletePackagingIntegration(id)
+
+	s.Assert().Equal(err, expectedErr)
+}
diff --git a/packages/operator/pkg/service/route/route_service_test.go b/packages/operator/pkg/service/route/route_service_test.go
index ad50011ea..9393a84a7 100644
--- a/packages/operator/pkg/service/route/route_service_test.go
+++ b/packages/operator/pkg/service/route/route_service_test.go
@@ -46,11 +46,11 @@ func TestSuiteRun(t *testing.T) {
 type TestSuite struct {
 	suite.Suite
 	mockRepo *mocks.Repository
-	service service.Service
-	db *sql.DB
-	dbMock sqlmock.Sqlmock
-	as *assert.Assertions
-	nilTx *sql.Tx
+	service  service.Service
+	db       *sql.DB
+	dbMock   sqlmock.Sqlmock
+	as       *assert.Assertions
+	nilTx    *sql.Tx
 	eMockPub *event_pub_mocks.EventPublisher
 }
 
@@ -166,7 +166,6 @@ func (s *TestSuite) TestUpdateModelRoute() {
 	ctx := context.Background()
 	en := newStubMT()
 
-
 	// Assume transaction commit
 	s.dbMock.ExpectBegin()
 	s.dbMock.ExpectCommit()
@@ -190,7 +189,6 @@ func (s *TestSuite) TestUpdateModelRoute_UpdatedAt() {
 	ctx := context.Background()
 	en := newStubMT()
 
-
 	// Assume transaction commit
 	s.dbMock.ExpectBegin()
 	s.dbMock.ExpectCommit()
@@ -263,7 +261,6 @@ func (s *TestSuite) TestUpdateModelRouteStatusSpecTouched() {
 	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
 	s.eMockPub.On("PublishEvent", ctx, mockTx, mock.Anything).Return(nil)
 
-
 	// Assume that repository return no error while set new status with not touched spec snapshot
 	newStatus := repoEn.Status
 	newStatus.EdgeURL = "old"
@@ -295,7 +292,7 @@ func (s *TestSuite) TestCreateModelRoute() {
 	en := newStubMT()
 	ctx := context.Background()
 	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
-	s.mockRepo.On("CreateModelRoute", ctx, mockTx, en).Return(nil)
+	s.mockRepo.On("SaveModelRoute", ctx, mockTx, en).Return(nil)
 	s.eMockPub.On("PublishEvent", ctx, mockTx, mock.Anything).Return(nil)
 
 	as.NoError(s.service.CreateModelRoute(ctx, en))
@@ -315,7 +312,7 @@ func (s *TestSuite) TestCreateModelRoute_CreatedAt() {
 
 	ctx := context.Background()
 	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
-	s.mockRepo.On("CreateModelRoute", ctx, mockTx, en).Return(nil)
+	s.mockRepo.On("SaveModelRoute", ctx, mockTx, en).Return(nil)
 	s.eMockPub.On("PublishEvent", ctx, mockTx, mock.Anything).Return(nil)
 
 	timeBeforeCall := time.Now()
@@ -339,7 +336,7 @@ func (s *TestSuite) TestCreateModelRoute_Error() {
 	ctx := context.Background()
 	anyError := errors.New("any error")
 	s.mockRepo.On("BeginTransaction", ctx).Return(mockTx, nil)
-	s.mockRepo.On("CreateModelRoute", ctx, mockTx, en).Return(anyError)
+	s.mockRepo.On("SaveModelRoute", ctx, mockTx, en).Return(anyError)
 	s.eMockPub.On("PublishEvent", ctx, mockTx, mock.Anything).Return(nil)
 
 	as.Error(s.service.CreateModelRoute(ctx, en))
@@ -361,4 +358,4 @@ func newStubMT() *apis.ModelRoute {
 		Spec:         v1alpha1.ModelRouteSpec{},
 		Status:       v1alpha1.ModelRouteStatus{},
 	}
-}
\ No newline at end of file
+}
diff --git a/packages/operator/pkg/service/toolchain/service_test.go b/packages/operator/pkg/service/toolchain/service_test.go
new file mode 100644
index 000000000..fd579b358
--- /dev/null
+++ b/packages/operator/pkg/service/toolchain/service_test.go
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2021 EPAM Systems
+ *
+ * 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 toolchain_test
+
+import (
+	"errors"
+	training3 "github.com/odahu/odahu-flow/packages/operator/pkg/apis/training"
+	toolchain_mock "github.com/odahu/odahu-flow/packages/operator/pkg/repository/training/mocks"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/service/toolchain"
+	"github.com/odahu/odahu-flow/packages/operator/pkg/utils/filter"
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/suite"
+	"testing"
+	"time"
+)
+
+func TestSuiteRun(t *testing.T) {
+	suite.Run(t, new(TITestSuite))
+}
+
+type TITestSuite struct {
+	suite.Suite
+	tiService  *toolchain.Service
+	tiRepoMock *toolchain_mock.ToolchainRepository
+}
+
+func (s *TITestSuite) SetupTest() {
+	s.tiRepoMock = &toolchain_mock.ToolchainRepository{}
+	s.tiService = toolchain.NewService(s.tiRepoMock)
+}
+
+// Tests that GetToolchainIntegration method proxies to repo
+func (s *TITestSuite) TestGetToolchainIntegration() {
+	id := "some-id"
+
+	expectedTI := &training3.ToolchainIntegration{}
+	expectedErr := errors.New("some-error")
+
+	s.tiRepoMock.On("GetToolchainIntegration", id).Return(expectedTI, expectedErr)
+
+	pi, err := s.tiService.GetToolchainIntegration(id)
+
+	s.Assert().Equal(pi, expectedTI)
+	s.Assert().Equal(err, expectedErr)
+}
+
+// Tests that GetToolchainIntegrationList method proxies to repo
+func (s *TITestSuite) TestGetToolchainIntegrationList() {
+	listOption := filter.Page(1)
+
+	expectedTIList := []training3.ToolchainIntegration{}
+	expectedErr := errors.New("some-error")
+
+	s.tiRepoMock.
+		On("GetToolchainIntegrationList", mock.AnythingOfType("filter.ListOption")).
+		Return(expectedTIList, expectedErr)
+
+	piList, err := s.tiService.GetToolchainIntegrationList(listOption)
+
+	s.Assert().Equal(piList, expectedTIList)
+	s.Assert().Equal(err, expectedErr)
+}
+
+// Tests that CreateToolchainIntegration method sets CreatedAt/UpdatedAt and proxies to repo
+func (s *TITestSuite) TestCreateToolchainIntegration() {
+	someTI := &training3.ToolchainIntegration{ID: "some-ti"}
+
+	expectedErr := errors.New("some-error")
+
+	s.tiRepoMock.
+		On("SaveToolchainIntegration", someTI).
+		Return(expectedErr)
+
+	timeBefore := time.Now()
+	err := s.tiService.CreateToolchainIntegration(someTI)
+
+	actualTI := s.tiRepoMock.Calls[0].Arguments.Get(0).(*training3.ToolchainIntegration)
+
+	s.Assert().Equal(someTI, actualTI)
+	s.Assert().Equal(err, expectedErr)
+
+	s.Assert().True(someTI.CreatedAt.After(timeBefore))
+	s.Assert().Equal(someTI.CreatedAt, someTI.UpdatedAt)
+}
+
+// Tests that CreateToolchainIntegration method sets CreatedAt/UpdatedAt and proxies to repo
+func (s *TITestSuite) TestUpdateToolchainIntegration() {
+	someTI := &training3.ToolchainIntegration{ID: "some-ti", CreatedAt: time.Now(), UpdatedAt: time.Now()}
+
+	expectedErr := errors.New("some-error")
+
+	s.tiRepoMock.
+		On("UpdateToolchainIntegration", someTI).
+		Return(expectedErr)
+
+	timeBeforeUpdate := time.Now()
+	err := s.tiService.UpdateToolchainIntegration(someTI)
+
+	actualPI := s.tiRepoMock.Calls[0].Arguments.Get(0).(*training3.ToolchainIntegration)
+
+	s.Assert().Equal(someTI, actualPI)
+	s.Assert().Equal(err, expectedErr)
+
+	s.Assert().True(someTI.UpdatedAt.After(timeBeforeUpdate))
+	s.Assert().True(someTI.CreatedAt.Before(timeBeforeUpdate))
+}
+
+// Tests that DeleteToolchainIntegration method proxies to repo
+func (s *TITestSuite) TestDeleteToolchainIntegration() {
+	id := "some-id"
+	expectedErr := errors.New("some-error")
+
+	s.tiRepoMock.On("DeleteToolchainIntegration", id).Return(expectedErr)
+
+	err := s.tiService.DeleteToolchainIntegration(id)
+
+	s.Assert().Equal(err, expectedErr)
+}
diff --git a/packages/operator/pkg/service/training/training_service_test.go b/packages/operator/pkg/service/training/training_service_test.go
index 567d7f348..155c8ee24 100644
--- a/packages/operator/pkg/service/training/training_service_test.go
+++ b/packages/operator/pkg/service/training/training_service_test.go
@@ -45,14 +45,13 @@ func TestSuiteRun(t *testing.T) {
 type TestSuite struct {
 	suite.Suite
 	mockRepo *mocks.Repository
-	service service.Service
-	db *sql.DB
-	dbMock sqlmock.Sqlmock
-	as *assert.Assertions
-	nilTx *sql.Tx
+	service  service.Service
+	db       *sql.DB
+	dbMock   sqlmock.Sqlmock
+	as       *assert.Assertions
+	nilTx    *sql.Tx
 }
 
-
 func (s *TestSuite) SetupSuite() {
 	s.as = assert.New(s.T())
 }
@@ -240,7 +239,7 @@ func (s *TestSuite) TestCreateModelTraining() {
 
 	en := newStubMT()
 	ctx := context.Background()
-	s.mockRepo.On("CreateModelTraining", ctx, s.nilTx, en).Return(nil)
+	s.mockRepo.On("SaveModelTraining", ctx, s.nilTx, en).Return(nil)
 	as.NoError(s.service.CreateModelTraining(ctx, en))
 	s.mockRepo.AssertExpectations(s.T())
 }
@@ -250,7 +249,7 @@ func (s *TestSuite) TestCreateModelTraining_CreatedAt() {
 
 	en := newStubMT()
 	ctx := context.Background()
-	s.mockRepo.On("CreateModelTraining", ctx, s.nilTx, en).Return(nil)
+	s.mockRepo.On("SaveModelTraining", ctx, s.nilTx, en).Return(nil)
 	timeBeforeCall := time.Now()
 	as.NoError(s.service.CreateModelTraining(ctx, en))
 	// CreatedAt, UpdatedAt fields must be updated on now during the invocation
@@ -265,7 +264,7 @@ func (s *TestSuite) TestCreateModelTraining_Error() {
 	en := newStubMT()
 	ctx := context.Background()
 	anyError := errors.New("any error")
-	s.mockRepo.On("CreateModelTraining", ctx, s.nilTx, en).Return(anyError)
+	s.mockRepo.On("SaveModelTraining", ctx, s.nilTx, en).Return(anyError)
 
 	as.Error(s.service.CreateModelTraining(ctx, en))
 	s.mockRepo.AssertExpectations(s.T())
@@ -285,4 +284,4 @@ func newStubMT() *apis.ModelTraining {
 		Spec:         v1alpha1.ModelTrainingSpec{},
 		Status:       v1alpha1.ModelTrainingStatus{},
 	}
-}
\ No newline at end of file
+}
diff --git a/packages/operator/pkg/testhelpers/stubclients/stub_clients.go b/packages/operator/pkg/testhelpers/stubclients/stub_clients.go
index 2b782ea26..2d45f1ce9 100644
--- a/packages/operator/pkg/testhelpers/stubclients/stub_clients.go
+++ b/packages/operator/pkg/testhelpers/stubclients/stub_clients.go
@@ -12,7 +12,7 @@ type TIStubClient struct {
 	db map[string]training.ToolchainIntegration
 }
 
-func NewTIStubClient() TIStubClient{
+func NewTIStubClient() TIStubClient {
 	return TIStubClient{
 		db: make(map[string]training.ToolchainIntegration),
 	}
@@ -48,19 +48,18 @@ func (t TIStubClient) UpdateToolchainIntegration(md *training.ToolchainIntegrati
 
 func (t TIStubClient) CreateToolchainIntegration(md *training.ToolchainIntegration) error {
 	t.db[md.ID] = training.ToolchainIntegration{
-		ID:    md.ID,
-		Spec:  md.Spec,
+		ID:     md.ID,
+		Spec:   md.Spec,
 		Status: md.Status,
 	}
 	return nil
 }
 
-
 type PIStubClient struct {
 	db map[string]packaging.PackagingIntegration
 }
 
-func NewPIStubClient() PIStubClient{
+func NewPIStubClient() PIStubClient {
 	return PIStubClient{
 		db: make(map[string]packaging.PackagingIntegration),
 	}
@@ -100,10 +99,10 @@ func (p PIStubClient) UpdatePackagingIntegration(md *packaging.PackagingIntegrat
 	panic("implement me")
 }
 
-func (p PIStubClient) CreatePackagingIntegration(md *packaging.PackagingIntegration) error {
+func (p PIStubClient) SavePackagingIntegration(md *packaging.PackagingIntegration) error {
 	p.db[md.ID] = packaging.PackagingIntegration{
-		ID:    md.ID,
-		Spec:  md.Spec,
+		ID:     md.ID,
+		Spec:   md.Spec,
 		Status: md.Status,
 	}
 	return nil