Commit d55e781e by Guangbo Chen Committed by Guangbo

Bump mysql to v1.3.1

parent ae742dfe
.git
OWNERS
\ No newline at end of file
apiVersion: v1
name: mysql
version: 0.3.8
version: 1.3.1
appVersion: 5.7.14
description: Fast, reliable, scalable, and easy to use open-source relational database
system.
......@@ -8,7 +9,7 @@ keywords:
- database
- sql
home: https://www.mysql.com/
icon: https://www.mysql.com/common/logos/logo-mysql-170x115.png
icon: file://../logo-mysql.png
sources:
- https://github.com/kubernetes/charts
- https://github.com/docker-library/mysql
......
......@@ -4,4 +4,4 @@
## Introduction
This chart bootstraps a single node MySQL deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
This chart bootstraps a single node MySQL deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
\ No newline at end of file
labels:
io.cattle.role: project # options are cluster/project
categories:
- Database
- MySQL
questions:
- variable: defaultImage
default: "true"
default: true
description: "Use default Docker images"
label: Use Default Images
type: boolean
......@@ -11,15 +13,25 @@ questions:
group: "Container Images"
subquestions:
- variable: image
default: "mysql"
description: "Docker image name"
default: "ranchercharts/mysql"
description: "MySQL image name"
type: string
label: Image Name
- variable: imageTag
default: "5.7.14"
description: "Docker image tag"
description: "MySQL image tag"
type: string
label: Image Tag
- variable: busybox.image
default: "ranchercharts/busybox"
description: "Busybox image name"
type: string
label: Image Image Name
- variable: busybox.tag
default: "1.29.3"
description: "Busybox image tag"
type: string
label: Busybox Image Tag
- variable: mysqlDatabase
default: "admin"
description: "Name of database to create"
......@@ -60,6 +72,11 @@ questions:
description: "If undefined or null, uses the default StorageClass. Defaults to null."
type: storageclass
label: Default storageClass
- variable: persistence.existingClaim
default: ""
description: "If not empty, uses the specified existing PVC instead of creating new one"
type: pvc
label: Existing Persistent Volume Claim for mysql
- variable: service.port
default: "3306"
description: "Service port for access to MySQL"
......@@ -86,3 +103,16 @@ questions:
min: 30000
max: 32767
label: Service NodePort number
- variable: metrics.enabled
default: false
description: "Enable mysql prometheus metrics exporter"
label: Enable MySQL Metrics
type: boolean
show_subquestion_if: true
group: "Metrics"
subquestions:
- variable: metrics.serviceMonitor.enabled
default: false
description: "Set this to `true` to create ServiceMonitor for Prometheus operator"
type: boolean
label: Create MySQL ServiceMonitor for Prometheus Operator
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
{{ template "mysql.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
{{- if .Values.existingSecret }}
If you have not already created the mysql password secret:
kubectl create secret generic {{ .Values.existingSecret }} --namespace {{ .Release.Namespace }} --from-file=./mysql-root-password --from-file=./mysql-password
{{ else }}
To get your root password run:
MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "mysql.fullname" . }} -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)
{{- end }}
To connect to your database:
......@@ -25,13 +32,12 @@ To connect to your database directly from outside the K8s cluster:
{{- else if contains "ClusterIP" .Values.service.type }}
MYSQL_HOST=127.0.0.1
MYSQL_PORT={{ default "3306" .Values.service.port }}
MYSQL_PORT={{ .Values.service.port }}
# Execute the following commands to route the connection:
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "mysql.fullname" . }}" -o jsonpath="{.items[0].metadata.name}")
kubectl port-forward $POD_NAME {{ default "3306" .Values.service.port }}:{{ default "3306" .Values.service.port }}
# Execute the following command to route the connection:
kubectl port-forward svc/{{ template "mysql.fullname" . }} {{ .Values.service.port }}
{{- end }}
mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}
......@@ -9,8 +9,24 @@ Expand the name of the chart.
{{/*
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 "mysql.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- printf .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Generate chart secret name
*/}}
{{- define "mysql.secretName" -}}
{{ default (include "mysql.fullname" .) .Values.existingSecret }}
{{- end -}}
\ No newline at end of file
......@@ -2,10 +2,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "mysql.fullname" . }}
name: {{ template "mysql.fullname" . }}-configuration
namespace: {{ .Release.Namespace }}
data:
{{- range $key, $val := .Values.configurationFiles }}
{{ $key }}: |-
{{ $val | indent 4}}
{{- end }}
{{- end -}}
{{- end -}}
\ No newline at end of file
apiVersion: apps/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "mysql.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
app: {{ template "mysql.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
{{- with .Values.deploymentAnnotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
selector:
matchLabels:
app: {{ template "mysql.fullname" . }}
template:
metadata:
labels:
app: {{ template "mysql.fullname" . }}
{{- with .Values.podLabels }}
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.podAnnotations }}
annotations:
{{ toYaml . | indent 8 }}
{{- end }}
spec:
{{- if .Values.schedulerName }}
schedulerName: "{{ .Values.schedulerName }}"
{{- end }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets:
{{ toYaml .Values.imagePullSecrets | indent 8 }}
{{- end }}
{{- if .Values.priorityClassName }}
priorityClassName: "{{ .Values.priorityClassName }}"
{{- end }}
{{- if .Values.securityContext.enabled }}
securityContext:
fsGroup: {{ .Values.securityContext.fsGroup }}
runAsUser: {{ .Values.securityContext.runAsUser }}
{{- end }}
initContainers:
- name: "remove-lost-found"
image: "busybox:1.25.0"
image: "{{ .Values.busybox.image}}:{{ .Values.busybox.tag }}"
imagePullPolicy: {{ .Values.imagePullPolicy | quote }}
resources:
{{ toYaml .Values.initContainer.resources | indent 10 }}
command: ["rm", "-fr", "/var/lib/mysql/lost+found"]
volumeMounts:
- name: data
......@@ -24,40 +57,59 @@ spec:
{{- if .Values.persistence.subPath }}
subPath: {{ .Values.persistence.subPath }}
{{- end }}
{{- if .Values.extraInitContainers }}
{{ tpl .Values.extraInitContainers . | indent 6 }}
{{- end }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
{{- if .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
{{ toYaml .Values.tolerations | indent 8 }}
{{- end }}
containers:
- name: {{ template "mysql.fullname" . }}
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
imagePullPolicy: {{ .Values.imagePullPolicy | quote }}
{{- with .Values.args }}
args:
{{- range . }}
- {{ . | quote }}
{{- end }}
{{- end }}
resources:
{{ toYaml .Values.resources | indent 10 }}
env:
{{- if .Values.mysqlAllowEmptyPassword }}
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "true"
{{- else }}
{{- end }}
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "mysql.fullname" . }}
name: {{ template "mysql.secretName" . }}
key: mysql-root-password
{{- if .Values.mysqlAllowEmptyPassword }}
optional: true
{{- end }}
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "mysql.fullname" . }}
name: {{ template "mysql.secretName" . }}
key: mysql-password
{{- end }}
{{- if or .Values.mysqlAllowEmptyPassword (empty .Values.mysqlUser) }}
optional: true
{{- end }}
- name: MYSQL_USER
value: {{ default "" .Values.mysqlUser | quote }}
- name: MYSQL_DATABASE
value: {{ default "" .Values.mysqlDatabase | quote }}
{{- if .Values.timezone }}
- name: TZ
value: {{ .Values.timezone }}
{{- end }}
ports:
- name: mysql
containerPort: 3306
......@@ -100,14 +152,80 @@ spec:
subPath: {{ .Values.persistence.subPath }}
{{- end }}
{{- if .Values.configurationFiles }}
{{- range $key, $val := .Values.configurationFiles }}
- name: configurations
mountPath: /etc/mysql/conf.d
mountPath: {{ $.Values.configurationFilesPath }}{{ $key }}
subPath: {{ $key }}
{{- end -}}
{{- end }}
{{- if .Values.initializationFiles }}
- name: migrations
mountPath: /docker-entrypoint-initdb.d
{{- end }}
{{- if .Values.ssl.enabled }}
- name: certificates
mountPath: /ssl
{{- end }}
{{- if .Values.extraVolumeMounts }}
{{ tpl .Values.extraVolumeMounts . | indent 8 }}
{{- end }}
{{- if .Values.metrics.enabled }}
- name: metrics
image: "{{ .Values.metrics.image }}:{{ .Values.metrics.imageTag }}"
imagePullPolicy: {{ .Values.metrics.imagePullPolicy | quote }}
{{- if .Values.mysqlAllowEmptyPassword }}
command:
- 'sh'
- '-c'
- 'DATA_SOURCE_NAME="root@(localhost:3306)/" /bin/mysqld_exporter'
{{- else }}
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "mysql.secretName" . }}
key: mysql-root-password
command:
- 'sh'
- '-c'
- 'DATA_SOURCE_NAME="root:$MYSQL_ROOT_PASSWORD@(localhost:3306)/" /bin/mysqld_exporter'
{{- end }}
{{- range $f := .Values.metrics.flags }}
- {{ $f | quote }}
{{- end }}
ports:
- name: metrics
containerPort: 9104
livenessProbe:
httpGet:
path: /
port: metrics
initialDelaySeconds: {{ .Values.metrics.livenessProbe.initialDelaySeconds }}
timeoutSeconds: {{ .Values.metrics.livenessProbe.timeoutSeconds }}
readinessProbe:
httpGet:
path: /
port: metrics
initialDelaySeconds: {{ .Values.metrics.readinessProbe.initialDelaySeconds }}
timeoutSeconds: {{ .Values.metrics.readinessProbe.timeoutSeconds }}
resources:
{{ toYaml .Values.metrics.resources | indent 10 }}
{{- end }}
volumes:
{{- if .Values.configurationFiles }}
- name: configurations
configMap:
name: {{ template "mysql.fullname" . }}
name: {{ template "mysql.fullname" . }}-configuration
{{- end }}
{{- if .Values.initializationFiles }}
- name: migrations
configMap:
name: {{ template "mysql.fullname" . }}-initialization
{{- end }}
{{- if .Values.ssl.enabled }}
- name: certificates
secret:
secretName: {{ .Values.ssl.secret }}
{{- end }}
- name: data
{{- if .Values.persistence.enabled }}
......@@ -116,3 +234,6 @@ spec:
{{- else }}
emptyDir: {}
{{- end -}}
{{- if .Values.extraVolumes }}
{{ tpl .Values.extraVolumes . | indent 6 }}
{{- end }}
{{- if .Values.initializationFiles }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "mysql.fullname" . }}-initialization
namespace: {{ .Release.Namespace }}
data:
{{- range $key, $val := .Values.initializationFiles }}
{{ $key }}: |-
{{ $val | indent 4}}
{{- end }}
{{- end -}}
\ No newline at end of file
......@@ -3,6 +3,11 @@ kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: {{ template "mysql.fullname" . }}
namespace: {{ .Release.Namespace }}
{{- with .Values.persistence.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
labels:
app: {{ template "mysql.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
......
{{- if not .Values.existingSecret }}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "mysql.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
app: {{ template "mysql.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
......@@ -19,3 +21,25 @@ data:
{{ else }}
mysql-password: {{ randAlphaNum 10 | b64enc | quote }}
{{ end }}
{{- if .Values.ssl.enabled }}
{{ if .Values.ssl.certificates }}
{{- range .Values.ssl.certificates }}
---
apiVersion: v1
kind: Secret
metadata:
name: {{ .name }}
labels:
app: {{ template "mysql.fullname" $ }}
chart: "{{ $.Chart.Name }}-{{ $.Chart.Version }}"
release: "{{ $.Release.Name }}"
heritage: "{{ $.Release.Service }}"
type: Opaque
data:
ca.pem: {{ .ca | b64enc }}
server-cert.pem: {{ .cert | b64enc }}
server-key.pem: {{ .key | b64enc }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
\ No newline at end of file
{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ include "mysql.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
app: {{ template "mysql.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
{{- if .Values.metrics.serviceMonitor.additionalLabels }}
{{ toYaml .Values.metrics.serviceMonitor.additionalLabels | indent 4 }}
{{- end }}
spec:
endpoints:
- port: metrics
interval: 30s
namespaceSelector:
matchNames:
- {{ .Release.Namespace }}
selector:
matchLabels:
app: {{ include "mysql.fullname" . }}
release: {{ .Release.Name }}
{{- end }}
......@@ -2,13 +2,24 @@ apiVersion: v1
kind: Service
metadata:
name: {{ template "mysql.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
app: {{ template "mysql.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
annotations:
{{- if .Values.service.annotations }}
{{ toYaml .Values.service.annotations | indent 4 }}
{{- end }}
{{- if and (.Values.metrics.enabled) (.Values.metrics.annotations) }}
{{ toYaml .Values.metrics.annotations | indent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
{{- if (and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP))) }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}
ports:
- name: mysql
port: {{ .Values.service.port }}
......@@ -16,5 +27,10 @@ spec:
{{- if .Values.service.nodePort }}
nodePort: {{ .Values.service.nodePort }}
{{- end }}
{{- if .Values.metrics.enabled }}
- name: metrics
port: 9104
targetPort: metrics
{{- end }}
selector:
app: {{ template "mysql.fullname" . }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "mysql.fullname" . }}-test
namespace: {{ .Release.Namespace }}
labels:
app: {{ template "mysql.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
heritage: "{{ .Release.Service }}"
release: "{{ .Release.Name }}"
data:
run.sh: |-
{{- if .Values.ssl.enabled | and .Values.mysqlRootPassword }}
@test "Testing SSL MySQL Connection" {
mysql --host={{ template "mysql.fullname" . }} --port={{ .Values.service.port | default "3306" }} --ssl-cert=/ssl/server-cert.pem --ssl-key=ssl/server-key.pem -u root -p{{ .Values.mysqlRootPassword }}
}
{{- else if .Values.mysqlRootPassword }}
@test "Testing MySQL Connection" {
mysql --host={{ template "mysql.fullname" . }} --port={{ .Values.service.port | default "3306" }} -u root -p{{ .Values.mysqlRootPassword }}
}
{{- end }}
apiVersion: v1
kind: Pod
metadata:
name: {{ template "mysql.fullname" . }}-test
namespace: {{ .Release.Namespace }}
labels:
app: {{ template "mysql.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
heritage: "{{ .Release.Service }}"
release: "{{ .Release.Name }}"
annotations:
"helm.sh/hook": test-success
spec:
initContainers:
- name: test-framework
image: "{{ .Values.testFramework.image}}:{{ .Values.testFramework.tag }}"
command:
- "bash"
- "-c"
- |
set -ex
# copy bats to tools dir
cp -R /usr/local/libexec/ /tools/bats/
volumeMounts:
- mountPath: /tools
name: tools
containers:
- name: {{ .Release.Name }}-test
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
command: ["/tools/bats/bats", "-t", "/tests/run.sh"]
volumeMounts:
- mountPath: /tests
name: tests
readOnly: true
- mountPath: /tools
name: tools
{{- if .Values.ssl.enabled }}
- name: certificates
mountPath: /ssl
{{- end }}
volumes:
- name: tests
configMap:
name: {{ template "mysql.fullname" . }}-test
- name: tools
emptyDir: {}
{{- if .Values.ssl.enabled }}
- name: certificates
secret:
secretName: {{ .Values.ssl.secret }}
{{- end }}
restartPolicy: Never
## mysql image version
## ref: https://hub.docker.com/r/library/mysql/tags/
##
image: "mysql"
image: "ranchercharts/mysql"
imageTag: "5.7.14"
busybox:
image: "ranchercharts/busybox"
tag: "1.29.3"
testFramework:
image: "ranchercharts/dduportal-bats"
tag: "0.4.0"
## Specify password for root user
##
## Default: random 10 character string
......@@ -12,6 +20,7 @@ imageTag: "5.7.14"
## Create a database user
##
# mysqlUser:
## Default: random 10 character string
# mysqlPassword:
## Allow unauthenticated access, uncomment to enable
......@@ -28,9 +37,38 @@ imageTag: "5.7.14"
##
imagePullPolicy: IfNotPresent
## Additionnal arguments that are passed to the MySQL container.
## For example use --default-authentication-plugin=mysql_native_password if older clients need to
## connect to a MySQL 8 instance.
args: []
extraVolumes: |
# - name: extras
# emptyDir: {}
extraVolumeMounts: |
# - name: extras
# mountPath: /usr/share/extras
# readOnly: true
extraInitContainers: |
# - name: do-something
# image: busybox
# command: ['do', 'something']
# Optionally specify an array of imagePullSecrets.
# Secrets must be manually created in the namespace.
# ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod
# imagePullSecrets:
# - name: myRegistryKeySecretName
## Node selector
## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
nodeSelector: {}
## Tolerations for pod assignment
## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
##
tolerations: []
livenessProbe:
......@@ -60,6 +98,18 @@ persistence:
# storageClass: "-"
accessMode: ReadWriteOnce
size: 8Gi
annotations: {}
## Use an alternate scheduler, e.g. "stork".
## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/
##
# schedulerName:
## Security context
securityContext:
enabled: false
runAsUser: 999
fsGroup: 999
## Configure resource requests and limits
## ref: http://kubernetes.io/docs/user-guide/compute-resources/
......@@ -69,18 +119,95 @@ resources:
memory: 256Mi
cpu: 100m
# Custom mysql configuration files path
configurationFilesPath: /etc/mysql/conf.d/
# Custom mysql configuration files used to override default mysql settings
configurationFiles:
configurationFiles: {}
# mysql.cnf: |-
# [mysqld]
# skip-name-resolve
# ssl-ca=/ssl/ca.pem
# ssl-cert=/ssl/server-cert.pem
# ssl-key=/ssl/server-key.pem
# Custom mysql init SQL files used to initialize the database
initializationFiles: {}
# first-db.sql: |-
# CREATE DATABASE IF NOT EXISTS first DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
# second-db.sql: |-
# CREATE DATABASE IF NOT EXISTS second DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
metrics:
enabled: false
image: ranchercharts/prom-mysqld-exporter
imageTag: v0.10.0
imagePullPolicy: IfNotPresent
resources: {}
annotations: {}
# prometheus.io/scrape: "true"
# prometheus.io/port: "9104"
livenessProbe:
initialDelaySeconds: 15
timeoutSeconds: 5
readinessProbe:
initialDelaySeconds: 5
timeoutSeconds: 1
flags: []
serviceMonitor:
enabled: false
additionalLabels: {}
## Configure the service
## ref: http://kubernetes.io/docs/user-guide/services/
service:
annotations: {}
## Specify a service type
## ref: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types
type: ClusterIP
port: 3306
# nodePort: 32000
# loadBalancerIP:
ssl:
enabled: false
secret: mysql-ssl-certs
certificates:
# - name: mysql-ssl-certs
# ca: |-
# -----BEGIN CERTIFICATE-----
# ...
# -----END CERTIFICATE-----
# cert: |-
# -----BEGIN CERTIFICATE-----
# ...
# -----END CERTIFICATE-----
# key: |-
# -----BEGIN RSA PRIVATE KEY-----
# ...
# -----END RSA PRIVATE KEY-----
## Populates the 'TZ' system timezone environment variable
## ref: https://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html
##
## Default: nil (mysql will use image's default timezone, normally UTC)
## Example: 'Australia/Sydney'
# timezone:
# Deployment Annotations
deploymentAnnotations: {}
# To be added to the database server pod(s)
podAnnotations: {}
podLabels: {}
## Set pod priorityClassName
# priorityClassName: {}
## Init container resources defaults
initContainer:
resources:
requests:
memory: 10Mi
cpu: 10m
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment