feat: add ability to sync charts + use offline install for pxc-operator
diff --git a/.ansible-lint b/.ansible-lint
index 4643671..dcd1f0f 100644
--- a/.ansible-lint
+++ b/.ansible-lint
@@ -2,6 +2,7 @@
 exclude_paths:
   - .github
   - atmosphere
+  - charts
   - molecule
   - playbooks
 
diff --git a/charts/pxc-operator/.helmignore b/charts/pxc-operator/.helmignore
new file mode 100644
index 0000000..50af031
--- /dev/null
+++ b/charts/pxc-operator/.helmignore
@@ -0,0 +1,22 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/pxc-operator/Chart.yaml b/charts/pxc-operator/Chart.yaml
new file mode 100644
index 0000000..5f412ff
--- /dev/null
+++ b/charts/pxc-operator/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+appVersion: 1.10.0
+description: A Helm chart for Deploying the Percona XtraDB Cluster Operator Kubernetes
+name: pxc-operator
+home: https://www.percona.com/doc/kubernetes-operator-for-pxc/kubernetes.html
+version: 1.10.0
+maintainers:
+  - name: cap1984
+    email: ivan.pylypenko@percona.com
+  - name: tplavcic
+    email: tomislav.plavcic@percona.com
+icon: https://artifacthub.io/image/0b8875cd-6661-4269-9cf6-0fd92d59017b@1x
diff --git a/charts/pxc-operator/LICENSE.txt b/charts/pxc-operator/LICENSE.txt
new file mode 100644
index 0000000..6a31453
--- /dev/null
+++ b/charts/pxc-operator/LICENSE.txt
@@ -0,0 +1,13 @@
+Copyright 2019 Paul Czarkowski <username.taken@gmail.com>
+
+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.
\ No newline at end of file
diff --git a/charts/pxc-operator/README.md b/charts/pxc-operator/README.md
new file mode 100644
index 0000000..7fcaaa5
--- /dev/null
+++ b/charts/pxc-operator/README.md
@@ -0,0 +1,44 @@
+# pxс-operator: A chart for installing Percona Kubernetes Operator for Percona XtraDB Cluster
+
+This chart implements the Percona XtraDB Cluster Operator deployment. [Percona XtraDB Cluster](https://www.percona.com/doc/percona-xtradb-cluster/LATEST/index.html) is a database clustering solution for MySQL. The Operator itself can be found here:
+* <https://github.com/percona/percona-xtradb-cluster-operator>
+
+## Pre-requisites
+* Kubernetes 1.17+
+* PV support on the underlying infrastructure - only if you are provisioning persistent volume(s).
+* Helm v3
+
+## Deployment Details
+* <https://kubernetes.io/docs/concepts/workloads/controllers/deployment/>
+
+## Chart Details
+This chart will:
+* deploy a PXC Operator Pod for the further MySQL XtraDB Cluster creation in K8S.
+
+### Installing the Chart
+To install the chart with the `pxc` release name using a dedicated namespace (recommended):
+
+```sh
+helm repo add percona https://percona.github.io/percona-helm-charts/
+helm install my-operator percona/pxc-operator --version 1.10.0 --namespace my-namespace
+```
+
+The chart can be customized using the following configurable parameters:
+
+| Parameter                       | Description                                                             | Default                                          |
+| ------------------------------- | ------------------------------------------------------------------------| -------------------------------------------------|
+| `image`                         | PXC Operator Container image full path                                  | `percona/percona-xtradb-cluster-operator:1.10.0` |
+| `imagePullPolicy`               | PXC Operator Container pull policy                                      | `Always`                                         |
+| `imagePullSecrets`              | PXC Operator Pod pull secret                                            | `[]`                                             |
+| `replicaCount`                  | PXC Operator Pod quantity                                               | `1`                                              |
+| `tolerations`                   | List of node taints to tolerate                                         | `[]`                                             |
+| `resources`                     | Resource requests and limits                                            | `{}`                                             |
+| `nodeSelector`                  | Labels for Pod assignment                                               | `{}`                                             |
+
+Specify parameters using `--set key=value[,key=value]` argument to `helm install`
+
+Alternatively a YAML file that specifies the values for the parameters can be provided like this:
+
+```sh
+helm install pxc-operator -f values.yaml percona/pxc-operator
+```
diff --git a/charts/pxc-operator/crds/crd.yaml b/charts/pxc-operator/crds/crd.yaml
new file mode 100644
index 0000000..323fb00
--- /dev/null
+++ b/charts/pxc-operator/crds/crd.yaml
@@ -0,0 +1,608 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: perconaxtradbclusters.pxc.percona.com
+spec:
+  group: pxc.percona.com
+  names:
+    kind: PerconaXtraDBCluster
+    listKind: PerconaXtraDBClusterList
+    plural: perconaxtradbclusters
+    singular: perconaxtradbcluster
+    shortNames:
+    - pxc
+    - pxcs
+  scope: Namespaced
+  versions:
+    - name: v1alpha1
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+    - name: v1
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: haproxy
+        type: string
+        description: Ready haproxy nodes
+        jsonPath: .status.haproxy.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+        scale:
+          specReplicasPath: .spec.pxc.size
+          statusReplicasPath: .status.pxc.ready
+          labelSelectorPath: .status.pxc.labelSelectorPath
+    - name: v1-1-0
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+    - name: v1-2-0
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+    - name: v1-3-0
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+    - name: v1-4-0
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+    - name: v1-5-0
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+    - name: v1-6-0
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: haproxy
+        type: string
+        description: Ready haproxy nodes
+        jsonPath: .status.haproxy.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+    - name: v1-7-0
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: haproxy
+        type: string
+        description: Ready haproxy nodes
+        jsonPath: .status.haproxy.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+    - name: v1-8-0
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: haproxy
+        type: string
+        description: Ready haproxy nodes
+        jsonPath: .status.haproxy.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+        scale:
+          specReplicasPath: .spec.pxc.size
+          statusReplicasPath: .status.pxc.ready
+          labelSelectorPath: .status.pxc.labelSelectorPath
+    - name: v1-9-0
+      storage: false
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: haproxy
+        type: string
+        description: Ready haproxy nodes
+        jsonPath: .status.haproxy.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+        scale:
+          specReplicasPath: .spec.pxc.size
+          statusReplicasPath: .status.pxc.ready
+          labelSelectorPath: .status.pxc.labelSelectorPath
+    - name: v1-10-0
+      storage: true
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Endpoint
+        type: string
+        jsonPath: .status.host
+      - name: Status
+        type: string
+        jsonPath: .status.state
+      - name: PXC
+        type: string
+        description: Ready pxc nodes
+        jsonPath: .status.pxc.ready
+      - name: proxysql
+        type: string
+        description: Ready proxysql nodes
+        jsonPath: .status.proxysql.ready
+      - name: haproxy
+        type: string
+        description: Ready haproxy nodes
+        jsonPath: .status.haproxy.ready
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+        scale:
+          specReplicasPath: .spec.pxc.size
+          statusReplicasPath: .status.pxc.ready
+          labelSelectorPath: .status.pxc.labelSelectorPath
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: perconaxtradbclusterbackups.pxc.percona.com
+spec:
+  group: pxc.percona.com
+  names:
+    kind: PerconaXtraDBClusterBackup
+    listKind: PerconaXtraDBClusterBackupList
+    plural: perconaxtradbclusterbackups
+    singular: perconaxtradbclusterbackup
+    shortNames:
+    - pxc-backup
+    - pxc-backups
+  scope: Namespaced
+  versions:
+    - name: v1
+      storage: true
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Cluster
+        type: string
+        description: Cluster name
+        jsonPath: .spec.pxcCluster
+      - name: Storage
+        type: string
+        description: Storage name from pxc spec
+        jsonPath: .status.storageName
+      - name: Destination
+        type: string
+        description: Backup destination
+        jsonPath: .status.destination
+      - name: Status
+        type: string
+        description: Job status
+        jsonPath: .status.state
+      - name: Completed
+        description: Completed time
+        type: date
+        jsonPath: .status.completed
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: perconaxtradbclusterrestores.pxc.percona.com
+spec:
+  group: pxc.percona.com
+  names:
+    kind: PerconaXtraDBClusterRestore
+    listKind: PerconaXtraDBClusterRestoreList
+    plural: perconaxtradbclusterrestores
+    singular: perconaxtradbclusterrestore
+    shortNames:
+    - pxc-restore
+    - pxc-restores
+  scope: Namespaced
+  versions:
+    - name: v1
+      storage: true
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Cluster
+        type: string
+        description: Cluster name
+        jsonPath: .spec.pxcCluster
+      - name: Status
+        type: string
+        description: Job status
+        jsonPath: .status.state
+      - name: Completed
+        description: Completed time
+        type: date
+        jsonPath: .status.completed
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
+      subresources:
+        status: {}
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: perconaxtradbbackups.pxc.percona.com
+spec:
+  group: pxc.percona.com
+  names:
+    kind: PerconaXtraDBBackup
+    listKind: PerconaXtraDBBackupList
+    plural: perconaxtradbbackups
+    singular: perconaxtradbbackup
+    shortNames: []
+  scope: Namespaced
+  versions:
+    - name: v1alpha1
+      storage: true
+      served: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+            status:
+              type: object
+              x-kubernetes-preserve-unknown-fields: true
+      additionalPrinterColumns:
+      - name: Cluster
+        type: string
+        description: Cluster name
+        jsonPath: .spec.pxcCluster
+      - name: Storage
+        type: string
+        description: Storage name from pxc spec
+        jsonPath: .status.storageName
+      - name: Destination
+        type: string
+        description: Backup destination
+        jsonPath: .status.destination
+      - name: Status
+        type: string
+        description: Job status
+        jsonPath: .status.state
+      - name: Completed
+        description: Completed time
+        type: date
+        jsonPath: .status.completed
+      - name: Age
+        type: date
+        jsonPath: .metadata.creationTimestamp
diff --git a/charts/pxc-operator/templates/NOTES.txt b/charts/pxc-operator/templates/NOTES.txt
new file mode 100644
index 0000000..a105dda
--- /dev/null
+++ b/charts/pxc-operator/templates/NOTES.txt
@@ -0,0 +1,5 @@
+1. pxc-operator deployed.
+  If you would like to deploy an pxc-cluster set cluster.enabled to true in values.yaml
+  Check the pxc-operator logs
+    export POD=$(kubectl get pods -l app.kubernetes.io/name={{ template "pxc-operator.name" . }} --namespace {{ .Release.Namespace }} --output name)
+    kubectl logs $POD --namespace={{ .Release.Namespace }}
\ No newline at end of file
diff --git a/charts/pxc-operator/templates/_helpers.tpl b/charts/pxc-operator/templates/_helpers.tpl
new file mode 100644
index 0000000..2c3d515
--- /dev/null
+++ b/charts/pxc-operator/templates/_helpers.tpl
@@ -0,0 +1,56 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "pxc-operator.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "pxc-operator.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "pxc-operator.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Common labels
+*/}}
+{{- define "pxc-operator.labels" -}}
+app.kubernetes.io/name: {{ include "pxc-operator.name" . }}
+helm.sh/chart: {{ include "pxc-operator.chart" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end -}}
+
+{{/*
+Functions returns image URI according to parameters set
+*/}}
+{{- define "pxc-operator.image" -}}
+{{- if .Values.image }}
+{{- .Values.image }}
+{{- else }}
+{{- printf "%s:%s" .Values.operatorImageRepository .Chart.AppVersion }}
+{{- end }}
+{{- end -}}
\ No newline at end of file
diff --git a/charts/pxc-operator/templates/deployment.yaml b/charts/pxc-operator/templates/deployment.yaml
new file mode 100644
index 0000000..c42ee80
--- /dev/null
+++ b/charts/pxc-operator/templates/deployment.yaml
@@ -0,0 +1,90 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "pxc-operator.fullname" . }}
+  labels:
+{{ include "pxc-operator.labels" . | indent 4 }}
+spec:
+  replicas: {{ .Values.replicaCount }}
+  selector:
+    matchLabels:
+      app.kubernetes.io/component: operator
+      app.kubernetes.io/name: {{ include "pxc-operator.name" . }}
+      app.kubernetes.io/instance: {{ .Release.Name }}
+      app.kubernetes.io/part-of: {{ include "pxc-operator.name" . }}
+  strategy:
+    rollingUpdate:
+      maxUnavailable: 1
+    type: RollingUpdate
+  template:
+    metadata:
+      labels:
+        app.kubernetes.io/component: operator
+        app.kubernetes.io/name: {{ include "pxc-operator.name" . }}
+        app.kubernetes.io/instance: {{ .Release.Name }}
+        app.kubernetes.io/part-of: {{ include "pxc-operator.name" . }}
+    spec:
+      serviceAccountName: {{ include "pxc-operator.fullname" . }}
+    {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+    {{- end }}
+      terminationGracePeriodSeconds: 600
+      containers:
+        - name: {{ .Chart.Name }}
+          image: {{ include "pxc-operator.image" . }}
+          imagePullPolicy: {{ .Values.imagePullPolicy }}
+          ports:
+          - containerPort: 8080
+            name: metrics
+            protocol: TCP
+          command:
+          - percona-xtradb-cluster-operator
+          env:
+            - name: WATCH_NAMESPACE
+              {{- if .Values.watchAllNamespaces }}
+              value: ""
+              {{- else }}
+              value: {{ default .Release.Namespace .Values.watchNamespace }}
+              {{- end }}
+            - name: POD_NAME
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.name
+            - name: OPERATOR_NAME
+              value: {{ include "pxc-operator.fullname" . }}
+          livenessProbe:
+            failureThreshold: 3
+            httpGet:
+              path: /metrics
+              port: metrics
+              scheme: HTTP
+          resources:
+            {{- toYaml .Values.resources | nindent 12 }}
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+    {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+    {{- end }}
+    {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+    {{- end }}
+{{- if .Values.watchAllNamespaces }}
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "pxc-operator.name" . }}
+  labels:
+    name: {{ include "pxc-operator.name" . }}
+spec:
+  ports:
+    - port: 443
+      targetPort: 9443
+  selector:
+    app.kubernetes.io/name: {{ include "pxc-operator.name" . }}
+{{- end }}
diff --git a/charts/pxc-operator/templates/namespace.yaml b/charts/pxc-operator/templates/namespace.yaml
new file mode 100644
index 0000000..5de1cbc
--- /dev/null
+++ b/charts/pxc-operator/templates/namespace.yaml
@@ -0,0 +1,6 @@
+{{ if .Values.watchNamespace }}
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: {{ .Values.watchNamespace }}
+{{ end }}
\ No newline at end of file
diff --git a/charts/pxc-operator/templates/role-binding.yaml b/charts/pxc-operator/templates/role-binding.yaml
new file mode 100644
index 0000000..43ed7ca
--- /dev/null
+++ b/charts/pxc-operator/templates/role-binding.yaml
@@ -0,0 +1,37 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ include "pxc-operator.fullname" . }}
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: percona-xtradb-cluster-operator
+---
+{{- if or .Values.watchNamespace .Values.watchAllNamespaces }}
+kind: ClusterRoleBinding
+{{- else }}
+kind: RoleBinding
+{{- end }}
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: {{ include "pxc-operator.fullname" . }}
+  {{- if .Values.watchNamespace }}
+  namespace: {{ .Values.watchNamespace }}
+  {{- end }}
+  labels:
+{{ include "pxc-operator.labels" . | indent 4 }}
+subjects:
+- kind: ServiceAccount
+  name: {{ include "pxc-operator.fullname" . }}
+  {{- if or .Values.watchNamespace .Values.watchAllNamespaces }}
+  namespace: {{ .Release.Namespace }}
+  {{- end }}
+roleRef:
+  {{- if or .Values.watchNamespace .Values.watchAllNamespaces }}
+  kind: ClusterRole
+  {{- else }}
+  kind: Role
+  {{- end }}
+  name: {{ include "pxc-operator.fullname" . }}
+  apiGroup: rbac.authorization.k8s.io
diff --git a/charts/pxc-operator/templates/role.yaml b/charts/pxc-operator/templates/role.yaml
new file mode 100644
index 0000000..525f068
--- /dev/null
+++ b/charts/pxc-operator/templates/role.yaml
@@ -0,0 +1,114 @@
+{{- if or .Values.watchNamespace .Values.watchAllNamespaces }}
+kind: ClusterRole
+{{- else }}
+kind: Role
+{{- end }}
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: {{ include "pxc-operator.fullname" . }}
+  labels:
+{{ include "pxc-operator.labels" . | indent 4 }}
+rules:
+- apiGroups:
+  - pxc.percona.com
+  resources:
+  - perconaxtradbclusters
+  - perconaxtradbclusters/status
+  - perconaxtradbclusterbackups
+  - perconaxtradbclusterbackups/status
+  - perconaxtradbclusterrestores
+  - perconaxtradbclusterrestores/status
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - update
+  - patch
+  - delete
+{{- if or .Values.watchNamespace .Values.watchAllNamespaces }}
+- apiGroups:
+  - admissionregistration.k8s.io
+  resources:
+  - validatingwebhookconfigurations
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - update
+  - patch
+  - delete
+{{- end }}
+- apiGroups:
+  - ""
+  resources:
+  - pods
+  - pods/exec
+  - pods/log
+  - configmaps
+  - services
+  - persistentvolumeclaims
+  - secrets
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - update
+  - patch
+  - delete
+- apiGroups:
+  - apps
+  resources:
+  - deployments
+  - replicasets
+  - statefulsets
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - update
+  - patch
+  - delete
+- apiGroups:
+  - batch
+  resources:
+  - jobs
+  - cronjobs
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - update
+  - patch
+  - delete
+- apiGroups:
+  - policy
+  resources:
+  - poddisruptionbudgets
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - update
+  - patch
+  - delete
+- apiGroups:
+  - certmanager.k8s.io
+  - cert-manager.io
+  resources:
+  - issuers
+  - certificates
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - update
+  - patch
+  - delete
+  - deletecollection
diff --git a/charts/pxc-operator/values.yaml b/charts/pxc-operator/values.yaml
new file mode 100644
index 0000000..c243c19
--- /dev/null
+++ b/charts/pxc-operator/values.yaml
@@ -0,0 +1,46 @@
+# Default values for pxc-operator.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+
+operatorImageRepository: percona/percona-xtradb-cluster-operator
+imagePullPolicy: IfNotPresent
+image: ""
+
+# set if you want to specify a namespace to watch
+# defaults to `.Release.namespace` if left blank
+# watchNamespace:
+
+# set if operator should be deployed in cluster wide mode. defaults to false
+watchAllNamespaces: false
+
+# set if you want to use a different operator name
+# defaults to `percona-xtradb-cluster-operator`
+# operatorName:
+
+# set to false if you don't want the helm chart to
+# automatically create the CRD.
+createCRD: true
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+resources:
+  # We usually recommend not to specify default resources and to leave this as a conscious
+  # choice for the user. This also increases chances charts run on environments with little
+  # resources, such as Minikube. If you don't want to specify resources, comment the following
+  # lines and add the curly braces after 'resources:'.
+  limits:
+    cpu: 200m
+    memory: 500Mi
+  requests:
+    cpu: 100m
+    memory: 20Mi
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
diff --git a/hack/sync-charts.sh b/hack/sync-charts.sh
new file mode 100755
index 0000000..29b4a4d
--- /dev/null
+++ b/hack/sync-charts.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# Copyright (c) 2023 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.
+
+# This script is used to sync the charts from the upstream repositories into
+# the charts directory.  It is used to update the charts to the versions which
+# are defined in this file.
+
+set -xe
+
+# Determine the root path for Atmosphere
+ATMOSPHERE="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." >/dev/null 2>&1 && pwd )"
+
+# Create work directory to avoid cluttering up workspace
+WORKDIR=$(mktemp -d)
+function cleanup {
+  rm -rfv ${WORKDIR}
+}
+trap cleanup EXIT
+
+# Clean-up all of the existing charts
+rm -rfv ${ATMOSPHERE}/charts/*
+
+# Switch to folder where we will be syncing charts
+pushd ${WORKDIR}
+
+PERCONA_REF="25d8099e1e1f807b6bc90d8d92b6a31a6dff082b"
+curl -LO https://github.com/percona/percona-helm-charts/archive/${PERCONA_REF}.tar.gz
+tar --strip-components=2 -C ${ATMOSPHERE}/charts -xvzf ${PERCONA_REF}.tar.gz percona-helm-charts-${PERCONA_REF}/charts/pxc-operator
+
+# Switch back to original directory
+popd
diff --git a/roles/percona_xtradb_cluster_operator/defaults/main.yml b/roles/percona_xtradb_cluster_operator/defaults/main.yml
index 740006e..0deab7f 100644
--- a/roles/percona_xtradb_cluster_operator/defaults/main.yml
+++ b/roles/percona_xtradb_cluster_operator/defaults/main.yml
@@ -1,9 +1,6 @@
-percona_xtradb_cluster_operator_helm_repository_name: percona
-percona_xtradb_cluster_operator_helm_repository_url: https://percona.github.io/percona-helm-charts
-
 percona_xtradb_cluster_operator_helm_release_name: pxc-operator
-percona_xtradb_cluster_operator_helm_chart_name: pxc-operator
-percona_xtradb_cluster_operator_helm_chart_version: 1.10.0
+percona_xtradb_cluster_operator_helm_chart_path: "{{ role_path }}/../../charts/pxc-operator/"
+percona_xtradb_cluster_operator_helm_chart_ref: /usr/local/src/pxc-operator
 
 percona_xtradb_cluster_operator_helm_release_namespace: openstack
 percona_xtradb_cluster_operator_helm_values: {}
diff --git a/roles/percona_xtradb_cluster_operator/meta/main.yml b/roles/percona_xtradb_cluster_operator/meta/main.yml
index aaf5af5..cc00085 100644
--- a/roles/percona_xtradb_cluster_operator/meta/main.yml
+++ b/roles/percona_xtradb_cluster_operator/meta/main.yml
@@ -25,3 +25,7 @@
 
 dependencies:
   - role: defaults
+  - role: upload_helm_chart
+    vars:
+      upload_helm_chart_src: "{{ percona_xtradb_cluster_operator_helm_chart_path }}"
+      upload_helm_chart_dest: "{{ percona_xtradb_cluster_operator_helm_chart_ref }}"
diff --git a/roles/percona_xtradb_cluster_operator/tasks/main.yml b/roles/percona_xtradb_cluster_operator/tasks/main.yml
index 136a86b..92e8a73 100644
--- a/roles/percona_xtradb_cluster_operator/tasks/main.yml
+++ b/roles/percona_xtradb_cluster_operator/tasks/main.yml
@@ -1,9 +1,3 @@
-- name: Configure Helm repository
-  run_once: true
-  kubernetes.core.helm_repository:
-    name: "{{ percona_xtradb_cluster_operator_helm_repository_name }}"
-    repo_url: "{{ percona_xtradb_cluster_operator_helm_repository_url }}"
-
 # NOTE(mnaser): We should get rid of this task eventually as it is suspending
 #               the old HelmRelease and removing it to avoid uninstalling the
 #               Helm chart.
@@ -33,8 +27,7 @@
   run_once: true
   kubernetes.core.helm:
     name: "{{ percona_xtradb_cluster_operator_helm_release_name }}"
-    chart_ref: "{{ percona_xtradb_cluster_operator_helm_repository_name }}/{{ percona_xtradb_cluster_operator_helm_chart_name }}"
-    chart_version: "{{ percona_xtradb_cluster_operator_helm_chart_version }}"
+    chart_ref: "{{ percona_xtradb_cluster_operator_helm_chart_ref }}"
     release_namespace: "{{ percona_xtradb_cluster_operator_helm_release_namespace }}"
     create_namespace: true
     kubeconfig: /etc/kubernetes/admin.conf
diff --git a/roles/upload_helm_chart/README.md b/roles/upload_helm_chart/README.md
new file mode 100644
index 0000000..8a33d3e
--- /dev/null
+++ b/roles/upload_helm_chart/README.md
@@ -0,0 +1,7 @@
+# `upload_helm_chart`
+
+This role takes a Helm chart and uploads it from Atmosphere to the remote
+system which is running the Helm commands.
+
+Since we bundle all of the Helm charts into the Ansible collection, we need
+to upload the chart to the remote system before we can install it.
diff --git a/roles/upload_helm_chart/meta/main.yml b/roles/upload_helm_chart/meta/main.yml
new file mode 100644
index 0000000..626ff8f
--- /dev/null
+++ b/roles/upload_helm_chart/meta/main.yml
@@ -0,0 +1,24 @@
+# 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: Upload helm charts to remote system
+  license: Apache-2.0
+  min_ansible_version: 5.5.0
+  standalone: false
+  platforms:
+    - name: Ubuntu
+      versions:
+        - focal
diff --git a/roles/upload_helm_chart/tasks/main.yml b/roles/upload_helm_chart/tasks/main.yml
new file mode 100644
index 0000000..7f3f230
--- /dev/null
+++ b/roles/upload_helm_chart/tasks/main.yml
@@ -0,0 +1,7 @@
+- name: Upload Helm chart
+  ansible.builtin.copy:
+    src: "{{ upload_helm_chart_src }}"
+    dest: "{{ upload_helm_chart_dest }}"
+    owner: root
+    group: root
+    mode: 0644