Skip to content

Commit 3c75b78

Browse files
authored
feature: move mysql operator to this repo (#737)
Co-authored-by: Adam Sándor [email protected]
1 parent 3f68140 commit 3c75b78

File tree

17 files changed

+765
-4
lines changed

17 files changed

+765
-4
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# MySQL Schema Operator
2+
3+
This example shows how an operator can control resources outside of the Kubernetes cluster. In this case it will be
4+
managing MySQL schemas in an existing database server. This is a common scenario in many organizations where developers
5+
need to create schemas for different applications and environments, but the database server itself is managed by a
6+
different team. Using this operator a dev team can create a CR in their namespace and have a schema provisioned automatically.
7+
Access to the MySQL server is configured in the configuration of the operator, so admin access is restricted.
8+
9+
This is an example input:
10+
```yaml
11+
apiVersion: "mysql.sample.javaoperatorsdk/v1"
12+
kind: MySQLSchema
13+
metadata:
14+
name: mydb
15+
spec:
16+
encoding: utf8
17+
```
18+
19+
Creating this custom resource will prompt the operator to create a schema named `mydb` in the MySQL server and update
20+
the resource status with its URL. Once the resource is deleted, the operator will delete the schema. Obviously don't
21+
use it as is with real databases.
22+
23+
### Try
24+
25+
To try how the operator works you will need the following:
26+
* JDK installed (minimum version 11, tested with 11 and 15)
27+
* Maven installed (tested with 3.6.3)
28+
* A working Kubernetes cluster (tested with v1.15.9-gke.24)
29+
* kubectl installed (tested with v1.15.5)
30+
* Docker installed (tested with 19.03.8)
31+
* Container image registry
32+
33+
How to configure all the above depends heavily on where your Kubernetes cluster is hosted.
34+
If you use [minikube](https://minikube.sigs.k8s.io/docs/) you will need to configure kubectl and docker differently
35+
than if you'd use [GKE](https://cloud.google.com/kubernetes-engine/). You will have to read the documentation of your
36+
Kubernetes provider to figure this out.
37+
38+
Once you have the basics you can build and deploy the operator.
39+
40+
### Build & Deploy
41+
42+
1. We will be building the Docker image from the source code using Maven, so we have to configure the Docker registry
43+
where the image should be pushed. Do this in mysql-schema/pom.xml. In the example below I'm setting it to
44+
the [Container Registry](https://cloud.google.com/container-registry/) in Google Cloud Europe.
45+
46+
```xml
47+
<to>
48+
<image>eu.gcr.io/my-gcp-project/mysql-operator</image>
49+
</to>
50+
```
51+
52+
1. The following Maven command will build the JAR file, package it as a Docker image and push it to the registry.
53+
54+
`mvn jib:dockerBuild`
55+
56+
1. Deploy the test MySQL on your cluster if you want to use it. Note that if you have an already running MySQL server
57+
you want to use, you can skip this step, but you will have to configure the operator to use that server.
58+
59+
`kubectl apply -f k8s/mysql-db.yaml`
60+
1. Deploy the CRD:
61+
62+
`kubectl apply -f k8s/crd.yaml`
63+
64+
1. Make a copy of `k8s/operator.yaml` and replace ${DOCKER_REGISTRY} and ${OPERATOR_VERSION} to the
65+
right values. You will want to set `OPERATOR_VERSION` to the one used for building the Docker image. `DOCKER_REGISTRY` should
66+
be the same as you set the docker-registry property in your `pom.xml`.
67+
If you look at the environment variables you will notice this is where the access to the MySQL server is configured.
68+
The default values assume the server is running in another Kubernetes namespace (called `mysql`), uses the `root` user
69+
with a not very secure password. In case you want to use a different MySQL server, this is where you configure it.
70+
71+
1. Run `kubectl apply -f copy-of-operator.yaml` to deploy the operator. You can wait for the deployment to succeed using
72+
this command: `kubectl rollout status deployment mysql-schema-operator -w`. `-w` will cause kubectl to continuously monitor
73+
the deployment until you stop it.
74+
75+
1. Now you are ready to create some databases! To create a database schema called `mydb` just apply the `k8s/schema.yaml`
76+
file with kubectl: `kubectl apply -f k8s/schema.yaml`. You can modify the database name in the file to create more schemas.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: mysql
5+
spec:
6+
selector:
7+
matchLabels:
8+
app: mysql
9+
strategy:
10+
type: Recreate
11+
template:
12+
metadata:
13+
labels:
14+
app: mysql
15+
spec:
16+
containers:
17+
- image: mysql:5.6
18+
name: mysql
19+
env:
20+
# Use secret in real usage
21+
- name: MYSQL_ROOT_PASSWORD
22+
value: password
23+
ports:
24+
- containerPort: 3306
25+
name: mysql
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: mysql
5+
spec:
6+
ports:
7+
- port: 3306
8+
selector:
9+
app: mysql
10+
type: LoadBalancer
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: mysql-schema-operator
5+
---
6+
apiVersion: apps/v1
7+
kind: Deployment
8+
metadata:
9+
name: mysql-schema-operator
10+
namespace: mysql-schema-operator
11+
spec:
12+
selector:
13+
matchLabels:
14+
app: mysql-schema-operator
15+
replicas: 1 # we always run a single replica of the operator to avoid duplicate handling of events
16+
strategy:
17+
type: Recreate # during an upgrade the operator will shut down before the new version comes up to prevent two instances running at the same time
18+
template:
19+
metadata:
20+
labels:
21+
app: mysql-schema-operator
22+
spec:
23+
serviceAccountName: mysql-schema-operator # specify the ServiceAccount under which's RBAC persmissions the operator will be executed under
24+
containers:
25+
- name: operator
26+
image: ${DOCKER_REGISTRY}/mysql-schema-operator:${OPERATOR_VERSION}
27+
imagePullPolicy: Always
28+
ports:
29+
- containerPort: 80
30+
env:
31+
- name: MYSQL_HOST
32+
value: mysql.mysql # assuming the MySQL server runs in a namespace called "mysql" on Kubernetes
33+
- name: MYSQL_USER
34+
value: root
35+
- name: MYSQL_PASSWORD
36+
value: password # sample-level security
37+
readinessProbe:
38+
httpGet:
39+
path: /health # when this returns 200 the operator is considered up and running
40+
port: 8080
41+
initialDelaySeconds: 1
42+
timeoutSeconds: 1
43+
livenessProbe:
44+
httpGet:
45+
path: /health # when this endpoint doesn't return 200 the operator is considered broken and get's restarted
46+
port: 8080
47+
initialDelaySeconds: 30
48+
timeoutSeconds: 1
49+
50+
---
51+
apiVersion: v1
52+
kind: ServiceAccount
53+
metadata:
54+
name: mysql-schema-operator
55+
namespace: mysql-schema-operator
56+
57+
---
58+
apiVersion: rbac.authorization.k8s.io/v1beta1
59+
kind: ClusterRole
60+
metadata:
61+
name: mysql-schema-operator
62+
rules:
63+
- apiGroups:
64+
- mysql.sample.javaoperatorsdk
65+
resources:
66+
- schemas
67+
verbs:
68+
- "*"
69+
- apiGroups:
70+
- mysql.sample.javaoperatorsdk
71+
resources:
72+
- schemas/status
73+
verbs:
74+
- "*"
75+
- apiGroups:
76+
- apiextensions.k8s.io
77+
resources:
78+
- customresourcedefinitions
79+
verbs:
80+
- "get"
81+
- "list"
82+
- apiGroups:
83+
- ""
84+
resources:
85+
- secrets
86+
verbs:
87+
- "*"
88+
89+
---
90+
apiVersion: rbac.authorization.k8s.io/v1
91+
kind: ClusterRoleBinding
92+
metadata:
93+
name: mysql-schema-operator
94+
subjects:
95+
- kind: ServiceAccount
96+
name: mysql-schema-operator
97+
namespace: mysql-schema-operator
98+
roleRef:
99+
kind: ClusterRole
100+
name: mysql-schema-operator
101+
apiGroup: ""
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: "mysql.sample.javaoperatorsdk/v1"
2+
kind: MySQLSchema
3+
metadata:
4+
name: mydb
5+
spec:
6+
encoding: utf8

sample-operators/mysql-schema/pom.xml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>io.javaoperatorsdk</groupId>
9+
<artifactId>sample-operators</artifactId>
10+
<version>2.0.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>sample-mysql-schema-operator</artifactId>
14+
<name>Operator SDK - Samples - MySQL Schema</name>
15+
<description>Provisions Schemas in a MySQL database</description>
16+
<packaging>jar</packaging>
17+
18+
<properties>
19+
<maven.compiler.source>11</maven.compiler.source>
20+
<maven.compiler.target>11</maven.compiler.target>
21+
<jib-maven-plugin.version>3.1.4</jib-maven-plugin.version>
22+
</properties>
23+
24+
<dependencies>
25+
<dependency>
26+
<groupId>io.javaoperatorsdk</groupId>
27+
<artifactId>operator-framework</artifactId>
28+
<version>${project.version}</version>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.takes</groupId>
32+
<artifactId>takes</artifactId>
33+
<version>1.19</version>
34+
</dependency>
35+
<dependency>
36+
<groupId>mysql</groupId>
37+
<artifactId>mysql-connector-java</artifactId>
38+
<version>8.0.26</version>
39+
</dependency>
40+
<dependency>
41+
<groupId>io.fabric8</groupId>
42+
<artifactId>crd-generator-apt</artifactId>
43+
<scope>provided</scope>
44+
</dependency>
45+
<dependency>
46+
<groupId>org.apache.logging.log4j</groupId>
47+
<artifactId>log4j-slf4j-impl</artifactId>
48+
<version>2.13.3</version>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.junit.jupiter</groupId>
52+
<artifactId>junit-jupiter-api</artifactId>
53+
<scope>test</scope>
54+
</dependency>
55+
<dependency>
56+
<groupId>org.junit.jupiter</groupId>
57+
<artifactId>junit-jupiter-engine</artifactId>
58+
<scope>test</scope>
59+
</dependency>
60+
<dependency>
61+
<groupId>org.awaitility</groupId>
62+
<artifactId>awaitility</artifactId>
63+
<version>4.1.0</version>
64+
<scope>test</scope>
65+
</dependency>
66+
<dependency>
67+
<groupId>com.fasterxml.jackson.dataformat</groupId>
68+
<artifactId>jackson-dataformat-yaml</artifactId>
69+
<version>2.13.0</version>
70+
</dependency>
71+
</dependencies>
72+
73+
<build>
74+
<plugins>
75+
<plugin>
76+
<groupId>com.google.cloud.tools</groupId>
77+
<artifactId>jib-maven-plugin</artifactId>
78+
<version>${jib-maven-plugin.version}</version>
79+
<configuration>
80+
<from>
81+
<image>gcr.io/distroless/java:11</image>
82+
</from>
83+
<to>
84+
<image>mysql-schema-operator</image>
85+
</to>
86+
</configuration>
87+
</plugin>
88+
<plugin>
89+
<groupId>org.apache.maven.plugins</groupId>
90+
<artifactId>maven-compiler-plugin</artifactId>
91+
<version>3.8.1</version>
92+
</plugin>
93+
</plugins>
94+
</build>
95+
96+
</project>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.javaoperatorsdk.operator.sample;
2+
3+
import org.apache.commons.lang3.ObjectUtils;
4+
5+
public class MySQLDbConfig {
6+
7+
private final String host;
8+
private final String port;
9+
private final String user;
10+
private final String password;
11+
12+
public MySQLDbConfig(String host, String port, String user, String password) {
13+
this.host = host;
14+
this.port = port != null ? port : "3306";
15+
this.user = user;
16+
this.password = password;
17+
}
18+
19+
public static MySQLDbConfig loadFromEnvironmentVars() {
20+
if (ObjectUtils.anyNull(System.getenv("MYSQL_HOST"),
21+
System.getenv("MYSQL_USER"), System.getenv("MYSQL_PASSWORD"))) {
22+
throw new IllegalStateException("Mysql server parameters not defined");
23+
}
24+
return new MySQLDbConfig(System.getenv("MYSQL_HOST"),
25+
System.getenv("MYSQL_PORT"),
26+
System.getenv("MYSQL_USER"),
27+
System.getenv("MYSQL_PASSWORD"));
28+
}
29+
30+
public String getHost() {
31+
return host;
32+
}
33+
34+
public String getPort() {
35+
return port;
36+
}
37+
38+
public String getUser() {
39+
return user;
40+
}
41+
42+
public String getPassword() {
43+
return password;
44+
}
45+
}
46+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.javaoperatorsdk.operator.sample;
2+
3+
import io.fabric8.kubernetes.api.model.Namespaced;
4+
import io.fabric8.kubernetes.client.CustomResource;
5+
import io.fabric8.kubernetes.model.annotation.Group;
6+
import io.fabric8.kubernetes.model.annotation.Version;
7+
8+
@Group("mysql.sample.javaoperatorsdk")
9+
@Version("v1")
10+
public class MySQLSchema extends CustomResource<SchemaSpec, SchemaStatus> implements Namespaced {
11+
}

0 commit comments

Comments
 (0)