Skip to content
This repository was archived by the owner on May 28, 2021. It is now read-only.

Support MySQL Enterprise #226

Merged
merged 9 commits into from
Oct 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ in the format of `$USER-TIMESTAMP`. This will need to be remembered as this is
needed for a latter step or can be exported as the `$MYSQL_AGENT_VERSION`
envrionment variable.

```bash
$ export MYSQL_AGENT_VERSION=$(cat dist/version.txt)
```

## Create a namespace

Create the namespace that the operator will reside in. By default this is
Expand All @@ -38,11 +42,13 @@ ServiceAccounts, ClusterRoles, and ClusterRoleBindings for the operator to
function.

```bash
$ kubectl -n $USER apply \
-f contrib/manifests/custom-resource-definitions.yaml \
-f contrib/manifests/rbac.yaml
$ sed -e "s/<NAMESPACE>/${USER}/g" \
contrib/manifests/role-binding-template.yaml | kubectl -n $USER apply -f -
$ kubectl -n $USER apply -f contrib/manifests/custom-resource-definitions.yaml
```
```bash
$ sed -e "s/<NAMESPACE>/${USER}/g" contrib/manifests/rbac.yaml | kubectl -n $USER apply -f -
```
```bash
$ sed -e "s/<NAMESPACE>/${USER}/g" contrib/manifests/role-binding-template.yaml | kubectl -n $USER apply -f -
```

### Run the MySQL Operator
Expand All @@ -54,9 +60,6 @@ development purposes.
$ make run-dev
```

If you did not set an envrionment variable previously, prefix this command with
`MYSQL_AGENT_VERSION=` followed by the $USER-TIMESTAMP fortmatted version.

## Creating an InnoDB cluster

For the purpose of this document, we will create a cluster with 3 members with
Expand Down
53 changes: 53 additions & 0 deletions docs/enterprise-edition-example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Enterprise edition tutorial
This tutorial will explain how to create a MySQL cluster that runs the enterprise version of MySQL.

## Prerequisites

- The MySQL operator repository checked out locally.
- Access to a Docker registry that contains the enterprise version of MySQL.

## 01 - Create the Operator
You will need to create the following:

1. Custom resources
2. RBAC configuration <sup>*</sup>
3. The Operator
4. The Agent ServiceAccount & RoleBinding

The creation of these resources can be achieved by following the [introductory tutorial][1]; return here before creating a MySQL cluster.

## 02 - Create a secret with registry credentials
To be able to pull the MySQL Enterprise Edition from Docker it is necessary to provide credentials, these credentials must be supplied in the form of a Kubernetes secret.

- Remember the name of the secret *myregistrykey* as this will need to be used in step 03 when creating the cluster.
- If you are pulling the MySQL Enterprise image from a different registry than the one in the example then the secret must contain the relevant credentials for that registry.

>For alternative ways to create Kubernetes secrets see their documentation on [creating secrets from Docker configs](https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) or [creating secrets manually](https://kubernetes.io/docs/concepts/containers/images/#creating-a-secret-with-a-docker-config).

Enter your credentials into the following command and execute it to create a Kubernetes secret that will enable pulling images from the Docker store.
```
kubectl create secret docker-registry myregistrykey \
--docker-server=https://index.docker.io/v1/ \
--docker-username= \
--docker-password= \
--docker-email=
```
## 03 - Create your MySQL Cluster
Finally, create your MySQL Cluster with the required specifications entered under `spec:`

- The `repository:` field should be the path to a Docker registry containing the enterprise edition of MySQL. If this is omitted, the default is taken from the MySQL operator field `defaultMysqlServer:` which you can also specify.
- The `imagePullSecrets`: field allows you to specify a list of Kubernetes secret names. These secret(s) should contain your credentials for the Docker registry.
- The version to be used should be specified, without this, a default version is used which is **not** guaranteed to match an available image of MySQL Enterprise.
- The namespace of the cluster must match the namespace of the RBAC permissions created in step 01.
```
kubectl apply -f examples/cluster/cluster-enterprise-version.yaml
```
### Check that it is running
You can now run the following command to access the SQL prompt in your MySQL Cluster, just replace `<NAMESPACE>` with the namespace you created your cluster in.
```
sh hack/mysql.sh <NAMESPACE>/mysql-0
```

><sup>*</sup>If you run into issues when creating RBAC roles see [Access controls](https://docs.cloud.oracle.com/iaas/Content/ContEng/Concepts/contengabouta]ccesscontrol.htm?) for more information.

[1]: docs/tutorial.md
9 changes: 9 additions & 0 deletions examples/cluster/cluster-enterprise-version.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: mysql.oracle.com/v1alpha1
kind: Cluster
metadata:
name: mysql-enterprise
spec:
version: "8.0.11"
repository: store/oracle/mysql-enterprise-server
imagePullSecrets:
- name: myregistrykey
4 changes: 2 additions & 2 deletions pkg/apis/mysql/v1alpha1/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ func TestDefaultVersion(t *testing.T) {
cluster := &Cluster{}
cluster.EnsureDefaults()

if cluster.Spec.Version != defaultVersion {
t.Errorf("Expected default version to be %s but got %s", defaultVersion, cluster.Spec.Version)
if cluster.Spec.Version != DefaultVersion {
t.Errorf("Expected default version to be %s but got %s", DefaultVersion, cluster.Spec.Version)
}
}

Expand Down
14 changes: 8 additions & 6 deletions pkg/apis/mysql/v1alpha1/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ import (
)

const (
// The default MySQL version to use if not specified explicitly by user
defaultVersion = "8.0.12"
// DefaultVersion is the MySQL version to use if not specified explicitly by user
DefaultVersion = "8.0.12"
defaultMembers = 3
defaultBaseServerID = 1000
// maxBaseServerID is the maximum safe value for BaseServerID calculated
// as max MySQL server_id value - max Replication Group size.
maxBaseServerID uint32 = 4294967295 - 9
// MysqlServer is the image to use if no image is specified explicitly by the user.
MysqlServer = "mysql/mysql-server"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/MysqlServer/DefaultRepository/

)

const (
Expand All @@ -50,10 +52,10 @@ func getOperatorVersionLabel(labelMap map[string]string) string {
return labelMap[constants.MySQLOperatorVersionLabel]
}

// EnsureDefaults will ensure that if a user omits and fields in the
// EnsureDefaults will ensure that if a user omits any fields in the
// spec that are required, we set some sensible defaults.
// For example a user can choose to omit the version
// and number of members.
// For example a user can choose to omit the version and number of
// members.
func (c *Cluster) EnsureDefaults() *Cluster {
if c.Spec.Members == 0 {
c.Spec.Members = defaultMembers
Expand All @@ -64,7 +66,7 @@ func (c *Cluster) EnsureDefaults() *Cluster {
}

if c.Spec.Version == "" {
c.Spec.Version = defaultVersion
c.Spec.Version = DefaultVersion
}

return c
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/mysql/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const MinimumMySQLVersion = "8.0.11"
type ClusterSpec struct {
// Version defines the MySQL Docker image version.
Version string `json:"version"`
// Repository defines the image repository from which to pull the MySQL server image.
Repository string `json:"repository"`
// ImagePullSecret defines the name of the secret that contains the
// required credentials for pulling from the specified Repository.
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecret"`
// Members defines the number of MySQL instances in a cluster
Members int32 `json:"members,omitempty"`
// BaseServerID defines the base number used to create unique server_id
Expand Down
4 changes: 4 additions & 0 deletions pkg/controllers/cluster/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ func (m *MySQLController) syncHandler(key string) error {
return errors.Wrap(err, "validating Cluster")
}

if cluster.Spec.Repository == "" {
cluster.Spec.Repository = m.opConfig.Images.DefaultMySQLServerImage
}

operatorVersion := buildversion.GetBuildVersion()
// Ensure that the required labels are set on the cluster.
sel := combineSelectors(SelectorForCluster(cluster), SelectorForClusterOperatorVersion(operatorVersion))
Expand Down
22 changes: 12 additions & 10 deletions pkg/options/operator/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
package operator

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"

"github.com/golang/glog"
"github.com/oracle/mysql-operator/pkg/apis/mysql/v1alpha1"
"github.com/pkg/errors"
"github.com/spf13/pflag"
"gopkg.in/yaml.v2"
Expand All @@ -29,15 +31,15 @@ import (
)

const (
mysqlServer = "mysql/mysql-server"
mysqlAgent = "iad.ocir.io/oracle/mysql-agent"
mysqlAgent = "iad.ocir.io/oracle/mysql-agent"
)

// Images is the configuration of required MySQLOperator images. Remember to configure the appropriate
// credentials for the target repositories.
// credentials for the target repositories. The DefaultMySQLServerImage can be overridden on a per-cluster
// basis by setting the Repository field.
type Images struct {
MySQLServerImage string `yaml:"mysqlServer"`
MySQLAgentImage string `yaml:"mysqlAgent"`
MySQLAgentImage string `yaml:"mysqlAgent"`
DefaultMySQLServerImage string `yaml:"defaultMysqlServer"`
}

// MySQLOperatorOpts holds the options for the MySQLOperator.
Expand Down Expand Up @@ -65,7 +67,7 @@ type MySQLOperatorOpts struct {
MinResyncPeriod metav1.Duration `yaml:"minResyncPeriod"`
}

// MySQLOperatorOpts will create a new MySQLOperatorOpts. If a valid
// NewMySQLOperatorOpts will create a new MySQLOperatorOpts. If a valid
// config file is specified and exists, it will be used to initialise the
// server. Otherwise, a default server will be created.
//
Expand Down Expand Up @@ -106,12 +108,12 @@ func (s *MySQLOperatorOpts) EnsureDefaults() {
if &s.Images == nil {
s.Images = Images{}
}
if s.Images.MySQLServerImage == "" {
s.Images.MySQLServerImage = mysqlServer
}
if s.Images.MySQLAgentImage == "" {
s.Images.MySQLAgentImage = mysqlAgent
}
if s.Images.DefaultMySQLServerImage == "" {
s.Images.DefaultMySQLServerImage = v1alpha1.MysqlServer
}
if s.MinResyncPeriod.Duration <= 0 {
s.MinResyncPeriod = metav1.Duration{Duration: 12 * time.Hour}
}
Expand All @@ -122,7 +124,7 @@ func (s *MySQLOperatorOpts) AddFlags(fs *pflag.FlagSet) *pflag.FlagSet {
fs.StringVar(&s.KubeConfig, "kubeconfig", s.KubeConfig, "Path to Kubeconfig file with authorization and master location information.")
fs.StringVar(&s.Master, "master", s.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig).")
fs.StringVar(&s.Namespace, "namespace", metav1.NamespaceAll, "The namespace for which the MySQL operator manages MySQL clusters. Defaults to all.")
fs.StringVar(&s.Images.MySQLServerImage, "mysql-server-image", s.Images.MySQLServerImage, "The name of the target 'mysql-server' image. Defaults to: mysql/mysql-server.")
fs.StringVar(&s.Images.DefaultMySQLServerImage, "mysql-server-image", s.Images.DefaultMySQLServerImage, fmt.Sprintf("The default image repository to pull the MySQL Server image from (can be overridden on a per-cluster basis). Defaults to: %q.", v1alpha1.MysqlServer))
fs.StringVar(&s.Images.MySQLAgentImage, "mysql-agent-image", s.Images.MySQLAgentImage, "The name of the target 'mysql-agent' image. Defaults to: iad.ocir.io/oracle/mysql-agent.")
fs.DurationVar(&s.MinResyncPeriod.Duration, "min-resync-period", s.MinResyncPeriod.Duration, "The resync period in reflectors will be random between MinResyncPeriod and 2*MinResyncPeriod.")
return fs
Expand Down
7 changes: 2 additions & 5 deletions pkg/options/operator/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ func assertRequiredDefaults(t *testing.T, s MySQLOperatorOpts) {
if &s.Images == nil {
t.Error("MySQLOperatorServer.Images: was nil, expected a valid configuration.")
}
if s.Images.MySQLServerImage != mysqlServer {
t.Errorf("MySQLOperatorServer.Images.MySQLServerImage: was '%s', expected '%s'.", s.Images.MySQLServerImage, mysqlServer)
}
if s.Images.MySQLAgentImage != mysqlAgent {
t.Errorf("MySQLOperatorServer.Images.MySQLAgentImage: was '%s', expected '%s'.", s.Images.MySQLAgentImage, mysqlAgent)
}
Expand Down Expand Up @@ -66,8 +63,8 @@ func mockMySQLOperatorOpts() MySQLOperatorOpts {
Master: "some-master",
Hostname: "some-hostname",
Images: Images{
MySQLServerImage: "some-mysql-img",
MySQLAgentImage: "some-agent-img",
MySQLAgentImage: "some-agent-img",
DefaultMySQLServerImage: "mysql/mysql-server",
},
MinResyncPeriod: v1.Duration{Duration: 42},
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/resources/statefulsets/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ func NewForCluster(cluster *v1alpha1.Cluster, images operatoropts.Images, servic
}

containers := []v1.Container{
mysqlServerContainer(cluster, images.MySQLServerImage, rootPassword, members, baseServerID),
mysqlServerContainer(cluster, cluster.Spec.Repository, rootPassword, members, baseServerID),
mysqlAgentContainer(cluster, images.MySQLAgentImage, rootPassword, members)}

podLabels := map[string]string{
Expand Down Expand Up @@ -399,6 +399,9 @@ func NewForCluster(cluster *v1alpha1.Cluster, images operatoropts.Images, servic
},
}

if cluster.Spec.ImagePullSecrets != nil {
ss.Spec.Template.Spec.ImagePullSecrets = append(ss.Spec.Template.Spec.ImagePullSecrets, cluster.Spec.ImagePullSecrets...)
}
if cluster.Spec.VolumeClaimTemplate != nil {
ss.Spec.VolumeClaimTemplates = append(ss.Spec.VolumeClaimTemplates, *cluster.Spec.VolumeClaimTemplate)
}
Expand Down
36 changes: 36 additions & 0 deletions pkg/resources/statefulsets/statefulset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,40 @@ func TestClusterWithOnlyMysqlServerResourceRequirements(t *testing.T) {
assert.Equal(t, mysqlServerResourceRequirements, statefulSet.Spec.Template.Spec.Containers[0].Resources, "MySQL-Server container resource requirements do not match expected.")
assert.Nil(t, statefulSet.Spec.Template.Spec.Containers[1].Resources.Limits, "MySQL-Agent container has resource limits set which were not initially defined in the spec")
assert.Nil(t, statefulSet.Spec.Template.Spec.Containers[1].Resources.Requests, "MySQL-Agent container has resource requests set which were not initially defined in the spec")

}

func TestClusterEnterpriseImage(t *testing.T) {
cluster := &v1alpha1.Cluster{
Spec: v1alpha1.ClusterSpec{
Repository: "some/image/path",
ImagePullSecrets: []corev1.LocalObjectReference{{
Name: "someSecretName",
}},
},
}
cluster.EnsureDefaults()

statefulSet := NewForCluster(cluster, mockOperatorConfig().Images, "mycluster")

pullSecrets := statefulSet.Spec.Template.Spec.ImagePullSecrets
ps := pullSecrets[len(pullSecrets)-1]
si := statefulSet.Spec.Template.Spec.Containers[0].Image

assert.Equal(t, "someSecretName", ps.Name)
assert.Equal(t, "some/image/path:"+v1alpha1.DefaultVersion, si)
}

func TestClusterDefaultOverride(t *testing.T) {
cluster := &v1alpha1.Cluster{}
cluster.EnsureDefaults()
cluster.Spec.Repository = "OverrideDefaultImage"

operatorConf := mockOperatorConfig()
operatorConf.Images.DefaultMySQLServerImage = "newDefaultImage"
statefulSet := NewForCluster(cluster, operatorConf.Images, "mycluster")

si := statefulSet.Spec.Template.Spec.Containers[0].Image

assert.Equal(t, "OverrideDefaultImage:"+v1alpha1.DefaultVersion, si)
}