Skip to content

Commit a719550

Browse files
authored
CLOUDP-57719: Add Basic Automation Config Struct (#6)
1 parent 43ced8e commit a719550

File tree

7 files changed

+319
-723
lines changed

7 files changed

+319
-723
lines changed

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ module github.com/mongodb/mongodb-kubernetes-operator
33
go 1.13
44

55
require (
6+
github.com/blang/semver v3.5.1+incompatible
7+
github.com/spf13/cast v1.3.1
8+
github.com/stretchr/testify v1.4.0
69
go.uber.org/zap v1.13.0
710
k8s.io/api v0.15.9
811
k8s.io/apimachinery v0.15.9

go.sum

Lines changed: 5 additions & 719 deletions
Large diffs are not rendered by default.

pkg/apis/mongodb/v1/mongodb_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var (
1717
type MongoDBSpec struct {
1818
// Members is the number of members in the replica set
1919
// +optional
20-
Members int32 `json:"members"`
20+
Members int `json:"members"`
2121
// Type defines which type of MongoDB deployment the resource should create
2222
Type Type `json:"type"`
2323
// Version defines which version of MongoDB will be used
@@ -54,7 +54,7 @@ func (m MongoDB) BuildStatefulSet() appsv1.StatefulSet {
5454
labels := map[string]string{
5555
"app": m.ServiceName(),
5656
}
57-
replicas := m.Spec.Members
57+
replicas := int32(m.Spec.Members)
5858
return appsv1.StatefulSet{
5959
ObjectMeta: metav1.ObjectMeta{
6060
Name: m.Name,
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package automationconfig
2+
3+
import (
4+
"path"
5+
)
6+
7+
type ProcessType string
8+
9+
const (
10+
Mongod ProcessType = "mongod"
11+
DefaultMongoDBDataDir = "/data"
12+
DefaultAgentLogPath = "/var/log/mongodb-mms-automation"
13+
)
14+
15+
type Auth struct {
16+
// Users is a list which contains the desired users at the project level.
17+
Users []MongoDBUser `json:"usersWanted,omitempty"`
18+
Disabled bool `json:"disabled"`
19+
// AuthoritativeSet indicates if the MongoDBUsers should be synced with the current list of Users
20+
AuthoritativeSet bool `json:"authoritativeSet"`
21+
// AutoAuthMechanisms is a list of auth mechanisms the Automation Agent is able to use
22+
AutoAuthMechanisms []string `json:"autoAuthMechanisms,omitempty"`
23+
24+
// AutoAuthMechanism is the currently active agent authentication mechanism. This is a read only
25+
// field
26+
AutoAuthMechanism string `json:"autoAuthMechanism"`
27+
// DeploymentAuthMechanisms is a list of possible auth mechanisms that can be used within deployments
28+
DeploymentAuthMechanisms []string `json:"deploymentAuthMechanisms,omitempty"`
29+
// AutoUser is the MongoDB Automation Agent user, when x509 is enabled, it should be set to the subject of the AA's certificate
30+
AutoUser string `json:"autoUser,omitempty"`
31+
// Key is the contents of the KeyFile, the automation agent will ensure this a KeyFile with these contents exists at the `KeyFile` path
32+
Key string `json:"key,omitempty"`
33+
// KeyFile is the path to a keyfile with read & write permissions. It is a required field if `Disabled=false`
34+
KeyFile string `json:"keyfile,omitempty"`
35+
// KeyFileWindows is required if `Disabled=false` even if the value is not used
36+
KeyFileWindows string `json:"keyfileWindows,omitempty"`
37+
// AutoPwd is a required field when going from `Disabled=false` to `Disabled=true`
38+
AutoPwd string `json:"autoPwd,omitempty"`
39+
}
40+
41+
func DisabledAuth() Auth {
42+
return Auth{
43+
Users: make([]MongoDBUser, 0),
44+
AutoAuthMechanisms: make([]string, 0),
45+
DeploymentAuthMechanisms: make([]string, 0),
46+
AutoAuthMechanism: "MONGODB-CR",
47+
Disabled: true,
48+
}
49+
}
50+
51+
type MongoDBUser struct {
52+
}
53+
54+
type Process struct {
55+
Name string `json:"name"`
56+
HostName string `json:"hostname"`
57+
Args26 Args26 `json:"args2_6"`
58+
Replication Replication `json:"replication"`
59+
Storage Storage `json:"storage"`
60+
ProcessType ProcessType `json:"processType"`
61+
Version string `json:"version"`
62+
AuthSchemaVersion int `json:"authSchemaVersion"`
63+
SystemLog SystemLog `json:"systemLog"`
64+
WiredTiger WiredTiger `json:"wiredTiger"`
65+
}
66+
67+
type SystemLog struct {
68+
Destination string `json:"destination"`
69+
Path string `json:"path"`
70+
}
71+
72+
func newProcess(name, hostName, version, replSetName string) Process {
73+
return Process{
74+
Name: name,
75+
HostName: hostName,
76+
Storage: Storage{
77+
DBPath: DefaultMongoDBDataDir,
78+
},
79+
Replication: Replication{ReplicaSetName: replSetName},
80+
ProcessType: Mongod,
81+
Version: version,
82+
SystemLog: SystemLog{
83+
Destination: "file",
84+
Path: path.Join(DefaultAgentLogPath, "/mongodb.log"),
85+
},
86+
}
87+
}
88+
89+
type Replication struct {
90+
ReplicaSetName string `json:"replSetName"`
91+
}
92+
93+
type Storage struct {
94+
DBPath string `json:"dbPath"`
95+
WiredTiger WiredTiger `json:"wiredTiger"`
96+
}
97+
98+
type WiredTiger struct {
99+
EngineConfig EngineConfig `json:"engineConfig"`
100+
}
101+
102+
type EngineConfig struct {
103+
CacheSizeGB float32 `json:"cacheSizeGB"`
104+
}
105+
106+
type LogRotate struct {
107+
SizeThresholdMB int `json:"sizeThresholdMB"`
108+
TimeThresholdHrs int `json:"timeThresholdHrs"`
109+
}
110+
111+
type Security struct {
112+
}
113+
114+
type Args26 struct {
115+
}
116+
117+
type ReplicaSet struct {
118+
Id string `json:"_id"`
119+
Members []ReplicaSetMember `json:"members"`
120+
ProtocolVersion string `json:"protocolVersion"`
121+
}
122+
123+
type ReplicaSetMember struct {
124+
Id string `json:"_id"`
125+
Host string `json:"host"`
126+
Priority int `json:"priority"`
127+
ArbiterOnly bool `json:"arbiterOnly"`
128+
Votes int `json:"votes"`
129+
}
130+
131+
func newReplicaSetMember(p Process, id string) ReplicaSetMember {
132+
return ReplicaSetMember{
133+
Id: id,
134+
Host: p.Name,
135+
Priority: 1,
136+
ArbiterOnly: false,
137+
Votes: 1,
138+
}
139+
}
140+
141+
type AutomationConfig struct {
142+
Version string `json:"version"`
143+
Processes []Process `json:"processes"`
144+
ReplicaSets []ReplicaSet `json:"replicaSets"`
145+
Auth Auth `json:"auth"`
146+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package automationconfig
2+
3+
import (
4+
"fmt"
5+
"github.com/spf13/cast"
6+
)
7+
8+
type Topology string
9+
10+
const (
11+
ReplicaSetTopology Topology = "ReplicaSet"
12+
)
13+
14+
type Builder struct {
15+
processes []Process
16+
replicaSets []ReplicaSet
17+
version string
18+
auth Auth
19+
members int
20+
domain string
21+
name string
22+
topology Topology
23+
mongodbVersion string
24+
}
25+
26+
func NewBuilder() *Builder {
27+
return &Builder{
28+
processes: []Process{},
29+
replicaSets: []ReplicaSet{},
30+
}
31+
}
32+
33+
func (b *Builder) SetTopology(topology Topology) *Builder {
34+
b.topology = topology
35+
return b
36+
}
37+
38+
func (b *Builder) SetMembers(members int) *Builder {
39+
b.members = members
40+
return b
41+
}
42+
43+
func (b *Builder) SetDomain(domain string) *Builder {
44+
b.domain = domain
45+
return b
46+
}
47+
48+
func (b *Builder) SetName(name string) *Builder {
49+
b.name = name
50+
return b
51+
}
52+
53+
func (b *Builder) SetMongoDBVersion(version string) *Builder {
54+
b.mongodbVersion = version
55+
return b
56+
}
57+
58+
func (b *Builder) SetAutomationConfigVersion(version int) *Builder {
59+
b.version = cast.ToString(version)
60+
return b
61+
}
62+
63+
func (b *Builder) Build() AutomationConfig {
64+
hostnames := make([]string, b.members)
65+
for i := 0; i < b.members; i++ {
66+
hostnames[i] = fmt.Sprintf("%s-%d.%s", b.name, i, b.domain)
67+
}
68+
69+
members := make([]ReplicaSetMember, b.members)
70+
processes := make([]Process, b.members)
71+
for i, h := range hostnames {
72+
process := newProcess(toHostName(b.name, i), h, b.mongodbVersion, b.name)
73+
processes[i] = process
74+
members[i] = newReplicaSetMember(process, cast.ToString(i))
75+
}
76+
77+
return AutomationConfig{
78+
Version: b.version,
79+
Processes: processes,
80+
ReplicaSets: []ReplicaSet{
81+
{
82+
Id: b.name,
83+
Members: members,
84+
ProtocolVersion: "1",
85+
},
86+
},
87+
Auth: DisabledAuth(),
88+
}
89+
}
90+
91+
func toHostName(name string, index int) string {
92+
return fmt.Sprintf("%s-%d", name, index)
93+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package automationconfig
2+
3+
import (
4+
"fmt"
5+
"github.com/spf13/cast"
6+
"github.com/stretchr/testify/assert"
7+
"testing"
8+
)
9+
10+
func TestBuildAutomationConfig(t *testing.T) {
11+
12+
ac := NewBuilder().
13+
SetName("my-rs").
14+
SetDomain("my-ns.svc.cluster.local").
15+
SetMongoDBVersion("4.2.0").
16+
SetAutomationConfigVersion(1).
17+
SetMembers(3).
18+
Build()
19+
20+
assert.Len(t, ac.Processes, 3)
21+
assert.Equal(t, "1", ac.Version)
22+
23+
for i, p := range ac.Processes {
24+
assert.Equal(t, Mongod, p.ProcessType)
25+
assert.Equal(t, fmt.Sprintf("my-rs-%d.my-ns.svc.cluster.local", i), p.HostName)
26+
assert.Equal(t, DefaultMongoDBDataDir, p.Storage.DBPath)
27+
assert.Equal(t, "my-rs", p.Replication.ReplicaSetName, "replication should be configured based on the replica set name provided")
28+
assert.Equal(t, toHostName("my-rs", i), p.Name)
29+
assert.Equal(t, "4.2.0", p.Version)
30+
}
31+
32+
assert.Len(t, ac.ReplicaSets, 1)
33+
rs := ac.ReplicaSets[0]
34+
assert.Equal(t, rs.Id, "my-rs", "The name provided should be configured to be the rs id")
35+
assert.Len(t, rs.Members, 3, "there should be the number of replicas provided")
36+
37+
for i, member := range rs.Members {
38+
assert.Equal(t, 1, member.Votes)
39+
assert.False(t, member.ArbiterOnly)
40+
assert.Equal(t, cast.ToString(i), member.Id)
41+
assert.Equal(t, ac.Processes[i].Name, member.Host)
42+
}
43+
}

pkg/controller/mongodb/mongodb_controller.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package mongodb
22

33
import (
44
"context"
5-
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/statefulset"
5+
"fmt"
66
mdbv1 "github.com/mongodb/mongodb-kubernetes-operator/pkg/apis/mongodb/v1"
7+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/automationconfig"
8+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/statefulset"
9+
"go.uber.org/zap"
710
"k8s.io/apimachinery/pkg/api/errors"
811
"k8s.io/apimachinery/pkg/runtime"
912
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -12,7 +15,6 @@ import (
1215
"sigs.k8s.io/controller-runtime/pkg/manager"
1316
"sigs.k8s.io/controller-runtime/pkg/reconcile"
1417
"sigs.k8s.io/controller-runtime/pkg/source"
15-
"go.uber.org/zap"
1618
)
1719

1820
// Add creates a new MongoDB Controller and adds it to the Manager. The Manager will set fields on the Controller
@@ -79,6 +81,22 @@ func (r *ReplicaSetReconciler) Reconcile(request reconcile.Request) (reconcile.R
7981
return reconcile.Result{}, err
8082
}
8183

84+
// TODO: Read current automation config version from config map
85+
86+
domain := getDomain(mdb.ServiceName(), mdb.Namespace, mdb.Name)
87+
_ = automationconfig.NewBuilder().
88+
SetTopology(automationconfig.ReplicaSetTopology).
89+
SetName(mdb.Name).
90+
SetDomain(domain).
91+
SetMembers(mdb.Spec.Members).
92+
SetMongoDBVersion(mdb.Spec.Version).
93+
SetAutomationConfigVersion(1).
94+
Build()
95+
96+
// TODO: Write the new config to the config map
97+
98+
// TODO: Create the service for the MDB resource
99+
82100
sts := mdb.BuildStatefulSet()
83101
if err := r.stsClient.CreateOrUpdate(sts); err != nil {
84102
log.Errorf("failed Creating/Updating StatefulSet: %s", err)
@@ -88,3 +106,10 @@ func (r *ReplicaSetReconciler) Reconcile(request reconcile.Request) (reconcile.R
88106
log.Info("Successfully finished reconciliation", "MongoDB.Spec:", mdb.Spec, "MongoDB.Status", mdb.Status)
89107
return reconcile.Result{}, nil
90108
}
109+
110+
func getDomain(service, namespace, clusterName string) string {
111+
if clusterName == "" {
112+
clusterName = "cluster.local"
113+
}
114+
return fmt.Sprint("%s.%s.svc.%s", service, namespace, clusterName)
115+
}

0 commit comments

Comments
 (0)