hm4: own hosting operator CRDs and RBAC

This commit is contained in:
Andrew Stoltz
2026-06-17 13:47:40 -05:00
parent 4f7a5f3d20
commit a0d79eeb8c
13 changed files with 1217 additions and 0 deletions

View File

@@ -0,0 +1,209 @@
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"metadata": {
"name": "mysql-operator",
"labels": {
"app.kubernetes.io/name": "mysql-operator",
"app.kubernetes.io/instance": "mysql-operator",
"app.kubernetes.io/managed-by": "flowercore",
"app.kubernetes.io/component": "operator",
"app.kubernetes.io/part-of": "flowercore-mysql"
}
},
"rules": [
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"mysqlinstancecrds"
],
"verbs": [
"get",
"list",
"watch",
"patch",
"update"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"mysqlinstancecrds/status"
],
"verbs": [
"get",
"patch",
"update"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"mysqlinstancecrds/finalizers"
],
"verbs": [
"get",
"patch",
"update"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"mysqlreplicacrds"
],
"verbs": [
"get",
"list",
"watch",
"patch",
"update",
"create",
"delete"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"mysqlreplicacrds/status"
],
"verbs": [
"get",
"patch",
"update"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"mysqlreplicacrds/finalizers"
],
"verbs": [
"get",
"patch",
"update"
]
},
{
"apiGroups": [
"apps"
],
"resources": [
"deployments"
],
"verbs": [
"get",
"list",
"create",
"update",
"delete"
]
},
{
"apiGroups": [
""
],
"resources": [
"services"
],
"verbs": [
"get",
"list",
"create",
"delete"
]
},
{
"apiGroups": [
""
],
"resources": [
"secrets"
],
"verbs": [
"get",
"list",
"create",
"delete"
]
},
{
"apiGroups": [
""
],
"resources": [
"persistentvolumeclaims"
],
"verbs": [
"get",
"list",
"create",
"delete"
]
},
{
"apiGroups": [
""
],
"resources": [
"namespaces"
],
"verbs": [
"get",
"list",
"create"
]
},
{
"apiGroups": [
"batch"
],
"resources": [
"jobs"
],
"verbs": [
"get",
"list",
"create"
]
},
{
"apiGroups": [
""
],
"resources": [
"events"
],
"verbs": [
"create",
"patch"
]
},
{
"apiGroups": [
"coordination.k8s.io"
],
"resources": [
"leases"
],
"verbs": [
"get",
"list",
"create",
"update"
]
}
]
}

View File

@@ -0,0 +1,154 @@
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"metadata": {
"name": "mysql-web"
},
"rules": [
{
"apiGroups": [
""
],
"resources": [
"namespaces",
"pods",
"services",
"secrets",
"configmaps",
"persistentvolumeclaims"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
},
{
"apiGroups": [
"apps"
],
"resources": [
"deployments",
"statefulsets"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
},
{
"apiGroups": [
""
],
"resources": [
"pods/log",
"pods/exec"
],
"verbs": [
"get",
"create"
]
},
{
"apiGroups": [
""
],
"resources": [
"nodes"
],
"verbs": [
"list"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"mysqlinstancecrds"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"mysqlreplicacrds"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"mysqlreplicacrds/status"
],
"verbs": [
"get",
"patch",
"update"
]
},
{
"apiGroups": [
"cert-manager.io"
],
"resources": [
"certificates"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
},
{
"apiGroups": [
"traefik.io"
],
"resources": [
"ingressroutes"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
}
]
}

View File

@@ -0,0 +1,133 @@
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"metadata": {
"name": "php-operator",
"labels": {
"app.kubernetes.io/name": "php-operator",
"app.kubernetes.io/instance": "php-operator",
"app.kubernetes.io/managed-by": "flowercore",
"app.kubernetes.io/component": "operator",
"app.kubernetes.io/part-of": "flowercore-php"
}
},
"rules": [
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"phpinstancecrds",
"phpapplicationcrds"
],
"verbs": [
"get",
"list",
"watch",
"patch",
"update"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"phpinstancecrds/status",
"phpapplicationcrds/status"
],
"verbs": [
"get",
"patch",
"update"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"phpinstancecrds/finalizers",
"phpapplicationcrds/finalizers"
],
"verbs": [
"get",
"patch",
"update"
]
},
{
"apiGroups": [
"apps"
],
"resources": [
"deployments"
],
"verbs": [
"get",
"list",
"create",
"update",
"delete"
]
},
{
"apiGroups": [
""
],
"resources": [
"services",
"secrets",
"persistentvolumeclaims",
"configmaps"
],
"verbs": [
"get",
"list",
"create",
"update",
"patch",
"delete"
]
},
{
"apiGroups": [
""
],
"resources": [
"namespaces"
],
"verbs": [
"get",
"list",
"create"
]
},
{
"apiGroups": [
""
],
"resources": [
"events"
],
"verbs": [
"create",
"patch"
]
},
{
"apiGroups": [
"coordination.k8s.io"
],
"resources": [
"leases"
],
"verbs": [
"get",
"list",
"create",
"update"
]
}
]
}

View File

@@ -0,0 +1,113 @@
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"metadata": {
"name": "php-web"
},
"rules": [
{
"apiGroups": [
""
],
"resources": [
"namespaces",
"pods",
"services",
"secrets",
"configmaps",
"persistentvolumeclaims"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
},
{
"apiGroups": [
"apps"
],
"resources": [
"deployments"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
},
{
"apiGroups": [
""
],
"resources": [
"pods/log",
"pods/exec"
],
"verbs": [
"get",
"create"
]
},
{
"apiGroups": [
"traefik.io"
],
"resources": [
"ingressroutes",
"middlewares"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
},
{
"apiGroups": [
"cert-manager.io"
],
"resources": [
"certificates"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
},
{
"apiGroups": [
"flowercore.io"
],
"resources": [
"phpapplicationcrds"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
}
]
}

View File

@@ -0,0 +1,26 @@
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"name": "mysql-operator",
"labels": {
"app.kubernetes.io/name": "mysql-operator",
"app.kubernetes.io/instance": "mysql-operator",
"app.kubernetes.io/managed-by": "flowercore",
"app.kubernetes.io/component": "operator",
"app.kubernetes.io/part-of": "flowercore-mysql"
}
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "mysql-operator"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "mysql-operator",
"namespace": "fc-system"
}
]
}

View File

@@ -0,0 +1,19 @@
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"name": "mysql-web"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "mysql-web",
"namespace": "fc-mysql"
}
],
"roleRef": {
"kind": "ClusterRole",
"name": "mysql-web",
"apiGroup": "rbac.authorization.k8s.io"
}
}

View File

@@ -0,0 +1,26 @@
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"name": "php-operator",
"labels": {
"app.kubernetes.io/name": "php-operator",
"app.kubernetes.io/instance": "php-operator",
"app.kubernetes.io/managed-by": "flowercore",
"app.kubernetes.io/component": "operator",
"app.kubernetes.io/part-of": "flowercore-php"
}
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "php-operator"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "php-operator",
"namespace": "fc-system"
}
]
}

View File

@@ -0,0 +1,19 @@
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"name": "php-web"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "php-web",
"namespace": "fc-php"
}
],
"roleRef": {
"kind": "ClusterRole",
"name": "php-web",
"apiGroup": "rbac.authorization.k8s.io"
}
}

View File

@@ -0,0 +1,151 @@
{
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": {
"name": "mysqlinstancecrds.flowercore.io"
},
"spec": {
"group": "flowercore.io",
"names": {
"kind": "MySqlInstanceCrd",
"listKind": "MySqlInstanceCrdList",
"plural": "mysqlinstancecrds",
"singular": "mysqlinstancecrd",
"shortNames": [
"mysql",
"mysqlinst"
]
},
"scope": "Namespaced",
"versions": [
{
"name": "v1",
"served": true,
"storage": true,
"schema": {
"openAPIV3Schema": {
"type": "object",
"properties": {
"spec": {
"type": "object",
"properties": {
"action": {
"type": "string",
"description": "Action to execute (e.g., provision, backup, restore, scale, decommission, healthcheck). Dispatched by the reconciler when this value changes."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Key-value labels for provisioning config (image-tag, mysql-version [deprecated], storage-size, replicas, port, max-connections) and billing/filtering."
}
}
},
"status": {
"type": "object",
"properties": {
"lastAction": {
"type": "string",
"description": "Name of the last action dispatched for this resource."
},
"lastActionStatus": {
"type": "string",
"description": "Outcome of the last action (Completed, Failed)."
},
"lastActionTimestamp": {
"type": "string",
"format": "date-time",
"description": "UTC timestamp when the last action was dispatched."
},
"message": {
"type": "string",
"description": "Human-readable message, typically populated on failure."
},
"ready": {
"type": "string",
"description": "Convenience field mirroring the Ready condition status (True, False, Unknown). Used by printer columns since JSONPath cannot filter condition arrays."
},
"retryCount": {
"type": "integer",
"description": "Number of consecutive transient failure retries for the current action. Reset to zero on success or when a new action is dispatched."
},
"nextRetryAt": {
"type": "string",
"format": "date-time",
"description": "UTC timestamp for the next retry attempt. Set on transient failure using exponential backoff (2^retryCount seconds, max 5 minutes)."
},
"conditions": {
"type": "array",
"description": "Kubernetes-style status conditions.",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "Condition type (e.g., Ready, Degraded)."
},
"status": {
"type": "string",
"description": "Condition status (True, False, Unknown)."
},
"lastTransitionTime": {
"type": "string",
"format": "date-time",
"description": "UTC timestamp of last status transition."
},
"reason": {
"type": "string",
"description": "Machine-readable reason code."
},
"message": {
"type": "string",
"description": "Human-readable details about the condition."
}
}
}
}
}
}
}
}
},
"subresources": {
"status": {}
},
"additionalPrinterColumns": [
{
"name": "Ready",
"type": "string",
"jsonPath": ".status.ready",
"description": "Whether the instance is ready"
},
{
"name": "Action",
"type": "string",
"jsonPath": ".spec.action",
"description": "Current action"
},
{
"name": "Status",
"type": "string",
"jsonPath": ".status.lastActionStatus",
"description": "Last action status"
},
{
"name": "Message",
"type": "string",
"jsonPath": ".status.message",
"description": "Status message",
"priority": 1
},
{
"name": "Age",
"type": "date",
"jsonPath": ".metadata.creationTimestamp"
}
]
}
]
}
}

View File

@@ -0,0 +1,167 @@
{
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": {
"name": "mysqlreplicacrds.flowercore.io"
},
"spec": {
"group": "flowercore.io",
"names": {
"kind": "MySqlReplicaCrd",
"listKind": "MySqlReplicaCrdList",
"plural": "mysqlreplicacrds",
"singular": "mysqlreplicacrd",
"shortNames": [
"mysqlreplica",
"mysqlrepl"
]
},
"scope": "Namespaced",
"versions": [
{
"name": "v1",
"served": true,
"storage": true,
"schema": {
"openAPIV3Schema": {
"type": "object",
"properties": {
"spec": {
"type": "object",
"properties": {
"action": {
"type": "string",
"description": "Reserved for ADR-039 action dispatch. The replica reconciler is declarative \u2014 promote / failover go through the MySQL Manager Web API instead."
},
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Replica topology fields carried as labels (primary-instance, replica-instance, replication-user, replication-user-secret, auto-failover)."
}
}
},
"status": {
"type": "object",
"properties": {
"phase": {
"type": "string",
"description": "Replication phase (Pending, Configuring, Running, Degraded, Failed, Promoted)."
},
"lastApplied": {
"type": "string",
"format": "date-time",
"description": "UTC timestamp of the last successful reconcile."
},
"currentSourceHost": {
"type": "string",
"description": "SOURCE_HOST currently configured on the replica."
},
"currentSourcePort": {
"type": "integer",
"description": "SOURCE_PORT currently configured on the replica."
},
"secondsBehindSource": {
"type": "integer",
"format": "int64",
"description": "Seconds_Behind_Source from SHOW REPLICA STATUS."
},
"replicaIoRunning": {
"type": "string",
"description": "Replica_IO_Running (Yes/No/Connecting)."
},
"replicaSqlRunning": {
"type": "string",
"description": "Replica_SQL_Running."
},
"lastError": {
"type": "string",
"description": "Most recent IO / SQL error text."
},
"lastAction": {
"type": "string"
},
"lastActionStatus": {
"type": "string"
},
"lastActionTimestamp": {
"type": "string",
"format": "date-time"
},
"message": {
"type": "string"
},
"ready": {
"type": "string"
},
"retryCount": {
"type": "integer"
},
"nextRetryAt": {
"type": "string",
"format": "date-time"
},
"conditions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"status": {
"type": "string"
},
"lastTransitionTime": {
"type": "string",
"format": "date-time"
},
"reason": {
"type": "string"
},
"message": {
"type": "string"
}
}
}
}
}
}
}
}
},
"subresources": {
"status": {}
},
"additionalPrinterColumns": [
{
"name": "Phase",
"type": "string",
"jsonPath": ".status.phase"
},
{
"name": "Source",
"type": "string",
"jsonPath": ".status.currentSourceHost"
},
{
"name": "Lag",
"type": "integer",
"jsonPath": ".status.secondsBehindSource"
},
{
"name": "Ready",
"type": "string",
"jsonPath": ".status.ready"
},
{
"name": "Age",
"type": "date",
"jsonPath": ".metadata.creationTimestamp"
}
]
}
]
}
}

View File

@@ -0,0 +1,85 @@
{
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": {
"name": "phpapplicationcrds.flowercore.io"
},
"spec": {
"group": "flowercore.io",
"names": {
"kind": "PhpApplicationCrd",
"listKind": "PhpApplicationCrdList",
"plural": "phpapplicationcrds",
"singular": "phpapplicationcrd",
"shortNames": [
"phpapp",
"phpapps"
]
},
"scope": "Namespaced",
"versions": [
{
"name": "v1",
"served": true,
"storage": true,
"schema": {
"openAPIV3Schema": {
"type": "object",
"properties": {
"spec": {
"type": "object",
"x-kubernetes-preserve-unknown-fields": true
},
"status": {
"type": "object",
"x-kubernetes-preserve-unknown-fields": true
}
}
}
},
"subresources": {
"status": {}
},
"additionalPrinterColumns": [
{
"name": "Ready",
"type": "string",
"jsonPath": ".status.ready",
"description": "Whether the application is ready"
},
{
"name": "Phase",
"type": "string",
"jsonPath": ".status.phase",
"description": "Current lifecycle phase"
},
{
"name": "Last",
"type": "string",
"jsonPath": ".status.lastCompletedPhase",
"description": "Last completed subphase"
},
{
"name": "Version",
"type": "string",
"jsonPath": ".status.version",
"description": "Installed application version",
"priority": 1
},
{
"name": "Error",
"type": "string",
"jsonPath": ".status.lastError",
"description": "Last reconcile error",
"priority": 1
},
{
"name": "Age",
"type": "date",
"jsonPath": ".metadata.creationTimestamp"
}
]
}
]
}
}

View File

@@ -0,0 +1,78 @@
{
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": {
"name": "phpinstancecrds.flowercore.io"
},
"spec": {
"group": "flowercore.io",
"names": {
"kind": "PhpInstanceCrd",
"listKind": "PhpInstanceCrdList",
"plural": "phpinstancecrds",
"singular": "phpinstancecrd",
"shortNames": [
"php",
"phpinst"
]
},
"scope": "Namespaced",
"versions": [
{
"name": "v1",
"served": true,
"storage": true,
"schema": {
"openAPIV3Schema": {
"type": "object",
"properties": {
"spec": {
"type": "object",
"x-kubernetes-preserve-unknown-fields": true
},
"status": {
"type": "object",
"x-kubernetes-preserve-unknown-fields": true
}
}
}
},
"subresources": {
"status": {}
},
"additionalPrinterColumns": [
{
"name": "Ready",
"type": "string",
"jsonPath": ".status.ready",
"description": "Whether the instance is ready"
},
{
"name": "Action",
"type": "string",
"jsonPath": ".spec.action",
"description": "Current action"
},
{
"name": "Status",
"type": "string",
"jsonPath": ".status.lastActionStatus",
"description": "Last action status"
},
{
"name": "Message",
"type": "string",
"jsonPath": ".status.message",
"description": "Status message",
"priority": 1
},
{
"name": "Age",
"type": "date",
"jsonPath": ".metadata.creationTimestamp"
}
]
}
]
}
}