fix: move memcached to operator
diff --git a/atmosphere/flows.py b/atmosphere/flows.py
index 81c223b..623dff0 100644
--- a/atmosphere/flows.py
+++ b/atmosphere/flows.py
@@ -103,6 +103,33 @@
             openstack_helm.CreateOrUpdateReleaseSecretTask(
                 namespace=NAMESPACE_OPENSTACK, chart="memcached"
             ),
+            openstack_helm.CreateOrUpdateHelmReleaseTask(
+                namespace=NAMESPACE_OPENSTACK,
+                repository=HELM_REPOSITORY_OPENSTACK_HELM_INFRA,
+                name="memcached",
+                version="0.1.12",
+            ),
+            kubernetes.CreateOrUpdateServiceTask(
+                namespace=NAMESPACE_OPENSTACK,
+                name="memcached-metrics",
+                labels={
+                    "application": "memcached",
+                    "component": "server",
+                },
+                spec={
+                    "selector": {
+                        "application": "memcached",
+                        "component": "server",
+                    },
+                    "ports": [
+                        {
+                            "name": "metrics",
+                            "port": 9150,
+                            "targetPort": 9150,
+                        }
+                    ],
+                },
+            ),
         )
 
     return flow
diff --git a/atmosphere/tasks/flux.py b/atmosphere/tasks/flux.py
index 51da589..1e4941f 100644
--- a/atmosphere/tasks/flux.py
+++ b/atmosphere/tasks/flux.py
@@ -63,17 +63,30 @@
         repository: str,
         chart: str,
         version: str,
-        values: dict,
+        values: dict = {},
+        values_from: list = [],
         *args,
         **kwargs,
     ):
+        kwargs.setdefault("requires", set())
+        kwargs["requires"] = kwargs["requires"].union(
+            set(
+                [
+                    "namespace",
+                    "name",
+                    "repository",
+                    "chart",
+                    "version",
+                    "values",
+                    "values_from",
+                ]
+            )
+        )
+
         super().__init__(
             HelmRelease,
             namespace,
             name,
-            requires=set(
-                ["namespace", "name", "repository", "chart", "version", "values"]
-            ),
             rebind={"repository": f"helm-repository-{namespace}-{repository}"},
             inject={
                 "name": name,
@@ -81,6 +94,7 @@
                 "chart": chart,
                 "version": version,
                 "values": values,
+                "values_from": values_from,
             },
             *args,
             **kwargs,
@@ -94,6 +108,7 @@
         chart: str,
         version: str,
         values: dict,
+        values_from: list,
         *args,
         **kwargs,
     ) -> HelmRelease:
@@ -118,10 +133,25 @@
                             },
                         }
                     },
+                    "install": {
+                        "disableWait": True,
+                    },
+                    "upgrade": {
+                        "disableWait": True,
+                    },
                     "values": values,
+                    "valuesFrom": values_from,
                 },
             },
         )
 
-    def update_object(self, resource: HelmRelease, values: dict = {}, *args, **kwargs):
+    def update_object(
+        self,
+        resource: HelmRelease,
+        values: dict = {},
+        values_from: list = [],
+        *args,
+        **kwargs,
+    ):
         resource.obj["spec"]["values"] = values
+        resource.obj["spec"]["valuesFrom"] = values_from
diff --git a/atmosphere/tasks/kubernetes.py b/atmosphere/tasks/kubernetes.py
index 8e790ec..3d743ec 100644
--- a/atmosphere/tasks/kubernetes.py
+++ b/atmosphere/tasks/kubernetes.py
@@ -101,6 +101,49 @@
         pass
 
 
+class CreateOrUpdateServiceTask(CreateOrUpdateKubernetesObjectTask):
+    def __init__(self, namespace: str, name: str, labels: dict, spec: dict):
+        super().__init__(
+            pykube.Service,
+            namespace,
+            name,
+            requires=set(["namespace", "name", "labels", "spec"]),
+            inject={"name": name, "labels": labels, "spec": spec},
+        )
+
+    def generate_object(
+        self,
+        namespace: pykube.Namespace,
+        name: str,
+        labels: dict,
+        spec: dict,
+        *args,
+        **kwargs,
+    ) -> pykube.Service:
+        return pykube.Service(
+            self.api,
+            {
+                "apiVersion": "v1",
+                "kind": "Service",
+                "metadata": {
+                    "name": name,
+                    "namespace": namespace.name,
+                    "labels": labels,
+                },
+                "spec": spec,
+            },
+        )
+
+    def update_object(
+        self,
+        resource: pykube.Service,
+        labels: dict,
+        spec: dict,
+    ):
+        resource.obj["metadata"]["labels"] = labels
+        resource.obj["spec"] = spec
+
+
 class CreateOrUpdateSecretTask(CreateOrUpdateKubernetesObjectTask):
     def __init__(self, namespace: str, name: str, data: str, *args, **kwargs):
         super().__init__(
diff --git a/atmosphere/tasks/openstack_helm.py b/atmosphere/tasks/openstack_helm.py
index ee6c6f3..afb92a6 100644
--- a/atmosphere/tasks/openstack_helm.py
+++ b/atmosphere/tasks/openstack_helm.py
@@ -1,5 +1,5 @@
 from atmosphere.models.openstack_helm import values
-from atmosphere.tasks import kubernetes
+from atmosphere.tasks import flux, kubernetes
 
 
 class CreateOrUpdateReleaseSecretTask(kubernetes.CreateOrUpdateSecretTask):
@@ -11,3 +11,27 @@
             *args,
             **kwargs,
         )
+
+
+class CreateOrUpdateHelmReleaseTask(flux.CreateOrUpdateHelmReleaseTask):
+    def __init__(
+        self,
+        namespace: str,
+        name: str,
+        repository: str,
+        version: str,
+    ):
+        super().__init__(
+            namespace=namespace,
+            name=name,
+            repository=repository,
+            chart=name,
+            version=version,
+            values_from=[
+                {
+                    "kind": "Secret",
+                    "name": f"atmosphere-{name}",
+                }
+            ],
+            requires=set([f"secret-{namespace}-atmosphere-{name}"]),
+        )
diff --git a/playbooks/openstack.yml b/playbooks/openstack.yml
index 98dc5eb..45eb83e 100644
--- a/playbooks/openstack.yml
+++ b/playbooks/openstack.yml
@@ -68,10 +68,6 @@
       tags:
         - percona-xtradb-cluster
 
-    - role: openstack_helm_infra_memcached
-      tags:
-        - openstack-helm-infra-memcached
-
     - role: rabbitmq_operator
       tags:
         - rabbitmq-operator
diff --git a/roles/openstack_helm_infra_memcached/README.md b/roles/openstack_helm_infra_memcached/README.md
deleted file mode 100644
index 326b981..0000000
--- a/roles/openstack_helm_infra_memcached/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# `openstack_helm_infra_memcached`
diff --git a/roles/openstack_helm_infra_memcached/meta/main.yml b/roles/openstack_helm_infra_memcached/meta/main.yml
deleted file mode 100644
index 8cc8938..0000000
--- a/roles/openstack_helm_infra_memcached/meta/main.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (c) 2022 VEXXHOST, Inc.
-#
-# 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.
-
-galaxy_info:
-  author: VEXXHOST, Inc.
-  description: Ansible role for Memcached
-  license: Apache-2.0
-  min_ansible_version: 5.5.0
-  standalone: false
-  platforms:
-    - name: Ubuntu
-      versions:
-        - focal
-
-dependencies:
-  - role: atmosphere
diff --git a/roles/openstack_helm_infra_memcached/tasks/main.yml b/roles/openstack_helm_infra_memcached/tasks/main.yml
deleted file mode 100644
index 9a21e7e..0000000
--- a/roles/openstack_helm_infra_memcached/tasks/main.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (c) 2022 VEXXHOST, Inc.
-#
-# 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.
-
-- name: Deploy Helm chart
-  kubernetes.core.k8s:
-    state: present
-    definition:
-      - apiVersion: helm.toolkit.fluxcd.io/v2beta1
-        kind: HelmRelease
-        metadata:
-          name: memcached
-          namespace: openstack
-        spec:
-          interval: 60s
-          chart:
-            spec:
-              chart: memcached
-              version: 0.1.6
-              sourceRef:
-                kind: HelmRepository
-                name: openstack-helm-infra
-          install:
-            disableWait: true
-          upgrade:
-            disableWait: true
-          valuesFrom:
-            - kind: Secret
-              name: atmosphere-memcached
-
-      - apiVersion: v1
-        kind: Service
-        metadata:
-          name: memcached-metrics
-          namespace: openstack
-          labels:
-            application: memcached
-            component: server
-        spec:
-          selector:
-            application: memcached
-            component: server
-          ports:
-            - name: metrics
-              port: 9150
-              targetPort: 9150