in mysqloperator/controller/innodbcluster/cluster_objects.py [0:0]
def prepare_cluster_stateful_set(cluster: InnoDBCluster, spec: AbstractServerSetSpec, logger: Logger) -> dict:
init_mysql_argv = ["mysqld", "--user=mysql"]
# if config.enable_mysqld_general_log:
# init_mysql_argv.append("--general-log=1")
mysql_argv = init_mysql_argv
# we only need this in initconf, we pass it to all operator images to be
# on the safe side
cluster_domain = k8s_cluster_domain(logger)
fqdn_template = fqdn.idc_service_fqdn_template(spec)
extra_label = ""
if type(spec) is InnoDBClusterSpec:
instance_type = "group-member"
elif type(spec) is ReadReplicaSpec:
instance_type = "read-replica"
extra_label = f"mysql.oracle.com/read-replica: {spec.name}"
# initial startup no replica, we scale up once the group is running
# spec.instances therefore will be reduced by the caller!
else:
raise NotImplementedError(f"Unknown subtype {type(spec)} for creating StatefulSet")
fixdatadir_container = ""
if spec.dataDirPermissions.setRightsUsingInitContainer:
fixdatadir_container = f"""
- name: fixdatadir
image: {spec.operator_image}
imagePullPolicy: {spec.sidecar_image_pull_policy}
command: ["bash", "-c", "chown 27:27 /var/lib/mysql && chmod 0700 /var/lib/mysql"]
securityContext:
# make an exception for this one
runAsNonRoot: false
runAsUser: 0
# These can't go to spec.template.spec.securityContext
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodTemplateSpec / https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSpec
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSecurityContext - for pods (top level)
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#Container
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#SecurityContext - for containers
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
capabilities:
add:
- CHOWN
- FOWNER
drop:
- ALL
volumeMounts:
- name: datadir
mountPath: /var/lib/mysql
env:
- name: MYSQL_OPERATOR_K8S_CLUSTER_DOMAIN
value: {cluster_domain}
- name: MYSQLSH_CREDENTIAL_STORE_SAVE_PASSWORDS
value: never
"""
logger.info(f"Fix data container {'EN' if fixdatadir_container else 'DIS'}ABLED")
# if meb restore ...
(restore_container, restore_volumes) = get_restore_container(cluster, spec, cluster_domain)
(meb_container, meb_volumes) = get_meb_container(cluster, spec, cluster_domain)
# TODO re-add "--log-file=",
tmpl = f"""
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {spec.name}
annotations:
mysql.oracle.com/fqdn-template: '{fqdn_template}'
labels:
tier: mysql
mysql.oracle.com/cluster: {spec.cluster_name}
mysql.oracle.com/instance-type: {instance_type}
{extra_label}
app.kubernetes.io/name: mysql-innodbcluster
app.kubernetes.io/instance: mysql-innodbcluster-{spec.name}
app.kubernetes.io/component: database
app.kubernetes.io/managed-by: mysql-operator
app.kubernetes.io/created-by: mysql-operator
spec:
serviceName: {spec.headless_service_name}
replicas: {spec.instances}
podManagementPolicy: Parallel
selector:
matchLabels:
component: mysqld
tier: mysql
mysql.oracle.com/cluster: {spec.cluster_name}
mysql.oracle.com/instance-type: {instance_type}
{extra_label}
app.kubernetes.io/name: mysql-innodbcluster-mysql-server
app.kubernetes.io/instance: mysql-innodbcluster-{spec.name}-mysql-server
app.kubernetes.io/component: database
app.kubernetes.io/managed-by: mysql-operator
app.kubernetes.io/created-by: mysql-operator
template:
metadata:
annotations:
mysql.oracle.com/fqdn-template: '{fqdn_template}'
labels:
component: mysqld
tier: mysql
mysql.oracle.com/cluster: {spec.cluster_name}
mysql.oracle.com/instance-type: {instance_type}
{extra_label}
app.kubernetes.io/name: mysql-innodbcluster-mysql-server
app.kubernetes.io/instance: mysql-innodbcluster-{spec.name}-mysql-server
app.kubernetes.io/component: database
app.kubernetes.io/managed-by: mysql-operator
app.kubernetes.io/created-by: mysql-operator
spec:
readinessGates:
- conditionType: "mysql.oracle.com/configured"
- conditionType: "mysql.oracle.com/ready"
serviceAccountName: {spec.serviceAccountName}
securityContext:
runAsUser: 27
runAsGroup: 27
fsGroup: 27
{utils.indent("fsGroupChangePolicy: " + spec.dataDirPermissions.fsGroupChangePolicy, 8) if spec.dataDirPermissions.fsGroupChangePolicy else ""}
runAsNonRoot: true
terminationGracePeriodSeconds: 120
initContainers:
{utils.indent(fixdatadir_container, 6)}
- name: initconf
image: {spec.operator_image}
imagePullPolicy: {spec.sidecar_image_pull_policy}
# For datadir see the datadir volum mount
command: ["mysqlsh", "--log-level=@INFO", "--pym", "mysqloperator", "init",
"--pod-name", "$(POD_NAME)",
"--pod-namespace", "$(POD_NAMESPACE)",
"--datadir", "/var/lib/mysql"
]
securityContext:
# These can't go to spec.template.spec.securityContext
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodTemplateSpec / https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSpec
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSecurityContext - for pods (top level)
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#Container
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#SecurityContext - for containers
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
# The value is is inherited from the PodSecurityContext but dumb sec checkers might not know that
runAsNonRoot: true
capabilities:
drop:
- ALL
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MYSQL_OPERATOR_K8S_CLUSTER_DOMAIN
value: {cluster_domain}
- name: MYSQLSH_USER_CONFIG_HOME
value: /tmp
- name: MYSQLSH_CREDENTIAL_STORE_SAVE_PASSWORDS
value: never
volumeMounts:
- name: initconfdir
mountPath: /mnt/initconf
readOnly: true
- name: datadir
mountPath: /var/lib/mysql
- name: mycnfdata
mountPath: /mnt/mycnfdata
- name: initconf-tmp
mountPath: /tmp
- name: rootcreds
readOnly: true
# rootHost is not obligatory and thus might not exist in the secret
# Nevertheless K8s won't complain and instead of mounting an empty file
# will create a directory (/rootcreds/rootHost will be an empty directory)
# For more information see below the comment regarding rootcreds.
subPath: rootHost
mountPath: /rootcreds/rootHost
{restore_container}
- name: initmysql
image: {spec.mysql_image}
imagePullPolicy: {spec.mysql_image_pull_policy}
args: {init_mysql_argv}
securityContext:
# These can't go to spec.template.spec.securityContext
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodTemplateSpec / https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSpec
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSecurityContext - for pods (top level)
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#Container
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#SecurityContext - for containers
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
# The value is is inherited from the PodSecurityContext but dumb sec checkers might not know that
runAsNonRoot: true
capabilities:
drop:
- ALL
env:
- name: MYSQL_INITIALIZE_ONLY
value: "1"
- name: MYSQL_RANDOM_ROOT_PASSWORD
value: "1"
- name: MYSQLSH_USER_CONFIG_HOME
value: /tmp
volumeMounts:
- name: datadir
mountPath: /var/lib/mysql
- name: rundir
mountPath: /var/run/mysqld
- name: mycnfdata
mountPath: /etc/my.cnf.d
subPath: my.cnf.d
- name: mycnfdata
mountPath: /docker-entrypoint-initdb.d
subPath: docker-entrypoint-initdb.d
- name: mycnfdata
mountPath: /etc/my.cnf
subPath: my.cnf
- name: initmysql-tmp
mountPath: /tmp
- name: varlibmysqlfiles # The entrypoint of the container `touch`-es 2 files there
mountPath: /var/lib/mysql-files
containers:
- name: sidecar
image: {spec.operator_image}
imagePullPolicy: {spec.sidecar_image_pull_policy}
command: ["mysqlsh", "--pym", "mysqloperator", "sidecar",
"--pod-name", "$(POD_NAME)",
"--pod-namespace", "$(POD_NAMESPACE)",
"--datadir", "/var/lib/mysql"
]
securityContext:
# These can't go to spec.template.spec.securityContext
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodTemplateSpec / https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSpec
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSecurityContext - for pods (top level)
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#Container
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#SecurityContext - for containers
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
# The value is is inherited from the PodSecurityContext but dumb sec checkers might not know that
runAsNonRoot: true
capabilities:
drop:
- ALL
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MYSQL_UNIX_PORT
value: /var/run/mysqld/mysql.sock
- name: MYSQLSH_USER_CONFIG_HOME
value: /mysqlsh
- name: MYSQL_OPERATOR_K8S_CLUSTER_DOMAIN
value: {cluster_domain}
- name: MYSQLSH_CREDENTIAL_STORE_SAVE_PASSWORDS
value: never
volumeMounts:
- name: rundir
mountPath: /var/run/mysqld
- name: mycnfdata
mountPath: /etc/my.cnf.d
subPath: my.cnf.d
- name: mycnfdata
mountPath: /etc/my.cnf
subPath: my.cnf
- name: shellhome
mountPath: /mysqlsh
- name: sidecar-tmp
mountPath: /tmp
{utils.indent(spec.extra_sidecar_volume_mounts, 8)}
- name: mysql
image: {spec.mysql_image}
imagePullPolicy: {spec.mysql_image_pull_policy}
args: {mysql_argv}
securityContext:
# These can't go to spec.template.spec.securityContext
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodTemplateSpec / https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSpec
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSecurityContext - for pods (top level)
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#Container
# See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#SecurityContext - for containers
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
# The value is is inherited from the PodSecurityContext but dumb sec checkers might not know that
runAsNonRoot: true
capabilities:
drop:
- ALL
lifecycle:
preStop:
exec:
# 60 is the default value for dba.gtidWaitTimeout
# see https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-innodb-cluster-working-with-cluster.html
command: ["sh", "-c", "sleep 60 && mysqladmin -ulocalroot shutdown"]
startupProbe:
exec:
command: ["/livenessprobe.sh", "8"]
initialDelaySeconds: 5
periodSeconds: 3
failureThreshold: 10000
successThreshold: 1
timeout: 2
readinessProbe:
exec:
command: ["/readinessprobe.sh"]
periodSeconds: 5
initialDelaySeconds: 10
failureThreshold: 10000
livenessProbe:
exec:
command: ["/livenessprobe.sh"]
initialDelaySeconds: 15
periodSeconds: 15
failureThreshold: 10
successThreshold: 1
timeout: 5
env:
- name: MYSQL_UNIX_PORT
value: /var/run/mysqld/mysql.sock
- name: MYSQLSH_CREDENTIAL_STORE_SAVE_PASSWORDS
value: never
{utils.indent(spec.extra_env, 8)}
ports:
- containerPort: {spec.mysql_port}
name: mysql
- containerPort: {spec.mysql_xport}
name: mysqlx
- containerPort: {spec.mysql_grport}
name: gr-xcom
volumeMounts:
- name: datadir
mountPath: /var/lib/mysql
- name: rundir
mountPath: /var/run/mysqld
- name: mycnfdata
mountPath: /etc/my.cnf.d
subPath: my.cnf.d
- name: mycnfdata
mountPath: /etc/my.cnf
subPath: my.cnf
- name: initconfdir
mountPath: /livenessprobe.sh
subPath: livenessprobe.sh
- name: initconfdir
mountPath: /readinessprobe.sh
subPath: readinessprobe.sh
- name: varlibmysqlfiles # The entrypoint of the container `touch`-es 2 files there
mountPath: /var/lib/mysql-files
- name: mysql-tmp
mountPath: /tmp
{utils.indent(spec.extra_volume_mounts, 8)}
{meb_container}
volumes:
- name: mycnfdata
emptyDir: {{}}
- name: rundir
emptyDir: {{}}
- name: varlibmysqlfiles
emptyDir: {{}}
- name: initconfdir
configMap:
name: {spec.name}-initconf
defaultMode: 0755
- name: shellhome
emptyDir: {{}}
- name: initconf-tmp
emptyDir: {{}}
- name: initmysql-tmp
emptyDir: {{}}
- name: mysql-tmp
emptyDir: {{}}
- name: sidecar-tmp
emptyDir: {{}}
{meb_volumes}
{restore_volumes}
# If we declare it and not use it anywhere as backing for a volumeMount K8s won't check
# if the volume exists. K8s seems to be lazy in that regard. We don't need the information
# from this secret directly, as the sidecar of pod 0 will fetch the information using the K8s API
# However, we won't not to be lazy in checking if the secret exists and make it easier for the
# administrator to find out if the secret is missing. If we mount it in a init or normal container,
# the pod # will get stuck into "Ready:0/2 Init:0/3" with
# Warning FailedMount XXs (....) kubelet "MountVolume.SetUp failed for volume "rootcreds" : secret ".........." not found" error to be seen in describe.
- name: rootcreds
secret:
secretName: {spec.secretName}
defaultMode: 0400
{utils.indent(spec.extra_volumes, 6)}
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 2Gi
"""
statefulset = yaml.safe_load(tmpl.replace("\n\n", "\n"))
metadata = {}
if spec.podAnnotations:
print("\t\tAdding podAnnotations")
metadata['annotations'] = spec.podAnnotations
if spec.podLabels:
print("\t\tAdding podLabels")
metadata['labels'] = spec.podLabels
if len(metadata):
utils.merge_patch_object(statefulset["spec"]["template"], {"metadata" : metadata })
if spec.keyring:
print("\t\tAdding keyring STS bit")
spec.keyring.add_to_sts_spec(statefulset)
for subsystem in spec.add_to_sts_cbs:
print(f"\t\tadd_to_sts_cb: Checking subsystem {subsystem}")
for add_to_sts_cb in spec.add_to_sts_cbs[subsystem]:
print(f"\t\tAdding {subsystem} STS bits")
add_to_sts_cb(statefulset, None, logger)
if os.getenv("MYSQL_OPERATOR_GLOBAL_PODSPEC_CM"):
ps_cm_ns = os.getenv("MYSQL_OPERATOR_GLOBAL_PODSPEC_NS", "mysql-operator")
ps_cm_name = os.getenv("MYSQL_OPERATOR_GLOBAL_PODSPEC_CM")
ps_cm_item = os.getenv("MYSQL_OPERATOR_GLOBAL_PODSPEC_ITEM", "podspec.yaml")
ps_cm = api_core.get_namespaced_config_map(ps_cm_name, ps_cm_name)
ps_override = yaml.safe_load(ps_cm[ps_cm_item])
print("\t\tAdding global podSpec")
utils.merge_patch_object(statefulset["spec"]["template"]["spec"],
ps_override, f"{ps_cm_ns}.{ps_cm_name}.{ps_cm_item}")
if spec.podSpec:
print("\t\tAdding podSpec")
utils.merge_patch_object(statefulset["spec"]["template"]["spec"],
spec.podSpec, "spec.podSpec")
if spec.datadirVolumeClaimTemplate:
print("\t\tAdding datadirVolumeClaimTemplate")
utils.merge_patch_object(statefulset["spec"]["volumeClaimTemplates"][0]["spec"],
spec.datadirVolumeClaimTemplate, "spec.volumeClaimTemplates[0].spec")
return statefulset