Skip to content
This repository was archived by the owner on Dec 12, 2025. It is now read-only.

Commit 7da4f08

Browse files
authored
Allow for arbitrary Status subresource updates (#179)
1 parent 2221afc commit 7da4f08

File tree

10 files changed

+476
-59
lines changed

10 files changed

+476
-59
lines changed

pkg/apis/mongodb/v1/mongodb_types.go

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ type Phase string
2424

2525
const (
2626
Running Phase = "Running"
27+
Failed Phase = "Failed"
28+
Pending Phase = "Pending"
2729
)
2830

2931
const (
@@ -197,6 +199,8 @@ type AuthMode string
197199
type MongoDBStatus struct {
198200
MongoURI string `json:"mongoUri"`
199201
Phase Phase `json:"phase"`
202+
Members int `json:"members"`
203+
Message string `json:"message,omitempty"`
200204
}
201205

202206
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -214,11 +218,6 @@ type MongoDB struct {
214218
Status MongoDBStatus `json:"status,omitempty"`
215219
}
216220

217-
func (m *MongoDB) UpdateSuccess() {
218-
m.Status.MongoURI = m.MongoURI()
219-
m.Status.Phase = Running
220-
}
221-
222221
// MongoURI returns a mongo uri which can be used to connect to this deployment
223222
func (m MongoDB) MongoURI() string {
224223
members := make([]string, m.Spec.Members)
@@ -229,17 +228,6 @@ func (m MongoDB) MongoURI() string {
229228
return fmt.Sprintf("mongodb://%s", strings.Join(members, ","))
230229
}
231230

232-
// TODO: this is a temporary function which will be used in the e2e tests
233-
// which will be removed in the following PR to clean up our mongo client testing
234-
func (m MongoDB) SCRAMMongoURI(username, password string) string {
235-
members := make([]string, m.Spec.Members)
236-
clusterDomain := "svc.cluster.local" // TODO: make this configurable
237-
for i := 0; i < m.Spec.Members; i++ {
238-
members[i] = fmt.Sprintf("%s-%d.%s.%s.%s:%d", m.Name, i, m.ServiceName(), m.Namespace, clusterDomain, 27017)
239-
}
240-
return fmt.Sprintf("mongodb://%s:%s@%s/?authMechanism=SCRAM-SHA-256", username, password, strings.Join(members, ","))
241-
}
242-
243231
func (m MongoDB) Hosts() []string {
244232
hosts := make([]string, m.Spec.Members)
245233
clusterDomain := "svc.cluster.local" // TODO: make this configurable
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package mongodb
2+
3+
import (
4+
"time"
5+
6+
mdbv1 "github.com/mongodb/mongodb-kubernetes-operator/pkg/apis/mongodb/v1"
7+
"go.uber.org/zap"
8+
9+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/util/status"
10+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
11+
)
12+
13+
// severity indicates the severity level
14+
// at which the message should be logged
15+
type severity string
16+
17+
const (
18+
Info severity = "INFO"
19+
Warn severity = "WARN"
20+
Error severity = "ERROR"
21+
None severity = "NONE"
22+
)
23+
24+
// optionBuilder is in charge of constructing a slice of options that
25+
// will be applied on top of the MongoDB resource that has been provided
26+
type optionBuilder struct {
27+
options []status.Option
28+
}
29+
30+
// GetOptions implements the OptionBuilder interface
31+
func (o *optionBuilder) GetOptions() []status.Option {
32+
return o.options
33+
}
34+
35+
// options returns an initialized optionBuilder
36+
func statusOptions() *optionBuilder {
37+
return &optionBuilder{
38+
options: []status.Option{},
39+
}
40+
}
41+
42+
func (o *optionBuilder) withMongoURI(uri string) *optionBuilder {
43+
o.options = append(o.options,
44+
mongoUriOption{
45+
mongoUri: uri,
46+
})
47+
return o
48+
}
49+
50+
type mongoUriOption struct {
51+
mongoUri string
52+
}
53+
54+
func (m mongoUriOption) ApplyOption(mdb *mdbv1.MongoDB) {
55+
mdb.Status.MongoURI = m.mongoUri
56+
}
57+
58+
func (m mongoUriOption) GetResult() (reconcile.Result, error) {
59+
return okResult()
60+
}
61+
62+
func (o *optionBuilder) withMembers(members int) *optionBuilder {
63+
o.options = append(o.options,
64+
membersOption{
65+
members: members,
66+
})
67+
return o
68+
}
69+
70+
type membersOption struct {
71+
members int
72+
}
73+
74+
func (m membersOption) ApplyOption(mdb *mdbv1.MongoDB) {
75+
mdb.Status.Members = m.members
76+
}
77+
78+
func (m membersOption) GetResult() (reconcile.Result, error) {
79+
return okResult()
80+
}
81+
func (o *optionBuilder) withPhase(phase mdbv1.Phase, retryAfter int) *optionBuilder {
82+
o.options = append(o.options,
83+
phaseOption{
84+
phase: phase,
85+
retryAfter: retryAfter,
86+
})
87+
return o
88+
}
89+
90+
type message struct {
91+
messageString string
92+
severityLevel severity
93+
}
94+
95+
type messageOption struct {
96+
message message
97+
}
98+
99+
func (m messageOption) ApplyOption(mdb *mdbv1.MongoDB) {
100+
mdb.Status.Message = m.message.messageString
101+
if m.message.severityLevel == Error {
102+
zap.S().Error(m.message)
103+
}
104+
if m.message.severityLevel == Warn {
105+
zap.S().Warn(m.message)
106+
}
107+
if m.message.severityLevel == Info {
108+
zap.S().Info(m.message)
109+
}
110+
}
111+
112+
func (m messageOption) GetResult() (reconcile.Result, error) {
113+
return okResult()
114+
}
115+
116+
func (o *optionBuilder) withMessage(severityLevel severity, msg string) *optionBuilder {
117+
o.options = append(o.options, messageOption{
118+
message: message{
119+
messageString: msg,
120+
severityLevel: severityLevel,
121+
},
122+
})
123+
return o
124+
}
125+
126+
func (o *optionBuilder) withFailedPhase() *optionBuilder {
127+
return o.withPhase(mdbv1.Failed, 0)
128+
}
129+
130+
func (o *optionBuilder) withPendingPhase(retryAfter int) *optionBuilder {
131+
return o.withPhase(mdbv1.Pending, retryAfter)
132+
}
133+
134+
func (o *optionBuilder) withRunningPhase() *optionBuilder {
135+
return o.withPhase(mdbv1.Running, -1)
136+
}
137+
138+
type phaseOption struct {
139+
phase mdbv1.Phase
140+
retryAfter int
141+
}
142+
143+
func (p phaseOption) ApplyOption(mdb *mdbv1.MongoDB) {
144+
mdb.Status.Phase = p.phase
145+
}
146+
147+
func (p phaseOption) GetResult() (reconcile.Result, error) {
148+
if p.phase == mdbv1.Running {
149+
return okResult()
150+
}
151+
if p.phase == mdbv1.Pending {
152+
return retryResult(p.retryAfter)
153+
}
154+
if p.phase == mdbv1.Failed {
155+
return failedResult()
156+
}
157+
return okResult()
158+
}
159+
160+
// helper functions which return reconciliation results which should be
161+
// returned from the main reconciliation loop
162+
163+
func okResult() (reconcile.Result, error) {
164+
return reconcile.Result{}, nil
165+
}
166+
167+
func retryResult(after int) (reconcile.Result, error) {
168+
return reconcile.Result{Requeue: true, RequeueAfter: time.Second * time.Duration(after)}, nil
169+
}
170+
171+
func failedResult() (reconcile.Result, error) {
172+
return retryResult(0)
173+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package mongodb
2+
3+
import (
4+
"testing"
5+
6+
mdbv1 "github.com/mongodb/mongodb-kubernetes-operator/pkg/apis/mongodb/v1"
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestMongoUriOption_ApplyOption(t *testing.T) {
13+
14+
mdb := newReplicaSet(3, "my-rs", "my-ns")
15+
16+
opt := mongoUriOption{
17+
mongoUri: "my-uri",
18+
}
19+
20+
opt.ApplyOption(&mdb)
21+
22+
assert.Equal(t, "my-uri", mdb.Status.MongoURI, "Status should be updated")
23+
}
24+
25+
func TestMembersOption_ApplyOption(t *testing.T) {
26+
mdb := newReplicaSet(3, "my-rs", "my-ns")
27+
28+
opt := membersOption{
29+
members: 5,
30+
}
31+
32+
opt.ApplyOption(&mdb)
33+
34+
assert.Equal(t, 3, mdb.Spec.Members, "Spec should remain unchanged")
35+
assert.Equal(t, 5, mdb.Status.Members, "Status should be updated")
36+
}
37+
38+
func TestOptionBuilder_RunningPhase(t *testing.T) {
39+
mdb := newReplicaSet(3, "my-rs", "my-ns")
40+
41+
statusOptions().withRunningPhase().GetOptions()[0].ApplyOption(&mdb)
42+
43+
assert.Equal(t, mdbv1.Running, mdb.Status.Phase)
44+
}
45+
46+
func TestOptionBuilder_PendingPhase(t *testing.T) {
47+
mdb := newReplicaSet(3, "my-rs", "my-ns")
48+
49+
statusOptions().withPendingPhase(10).GetOptions()[0].ApplyOption(&mdb)
50+
51+
assert.Equal(t, mdbv1.Pending, mdb.Status.Phase)
52+
}
53+
54+
func TestOptionBuilder_FailedPhase(t *testing.T) {
55+
mdb := newReplicaSet(3, "my-rs", "my-ns")
56+
57+
statusOptions().withFailedPhase().GetOptions()[0].ApplyOption(&mdb)
58+
59+
assert.Equal(t, mdbv1.Failed, mdb.Status.Phase)
60+
}
61+
62+
func newReplicaSet(members int, name, namespace string) mdbv1.MongoDB {
63+
return mdbv1.MongoDB{
64+
TypeMeta: metav1.TypeMeta{},
65+
ObjectMeta: metav1.ObjectMeta{
66+
Name: name,
67+
Namespace: namespace,
68+
},
69+
Spec: mdbv1.MongoDBSpec{
70+
Members: members,
71+
},
72+
}
73+
}

0 commit comments

Comments
 (0)