Skip to content

Commit b722629

Browse files
authored
Support namespace ExtendedOptions in cluster spec (#282)
> LGTM, but it would be good to test on a live operated cluster before we merge, both with and without extended options. I've seen some... "unintuitive" (to put it nicely) behavior from Kubernetes before when serializing / deserializing anything non-standard. Sure, we've done some testing on a real cluster.
1 parent 66671e8 commit b722629

File tree

5 files changed

+190
-21
lines changed

5 files changed

+190
-21
lines changed

.golangci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ linters:
211211
# New line required before return would require a large fraction of the
212212
# code base to need updating, it's not worth the perceived benefit.
213213
- nlreturn
214+
# Opinionated and sometimes wrong.
215+
- paralleltest
214216
disable-all: false
215217
presets:
216218
# bodyclose, errcheck, gosec, govet, scopelint, staticcheck, typecheck

pkg/apis/m3dboperator/v1alpha1/namespace.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
package v1alpha1
2222

23+
import "encoding/json"
24+
2325
// Namespace defines an M3DB namespace or points to a preset M3DB namespace.
2426
type Namespace struct {
2527
// Name is the namespace name.
@@ -98,6 +100,12 @@ type DownsampleOptions struct {
98100
All bool `json:"all,omitempty"`
99101
}
100102

103+
// ExtendedOptions stores the extended namespace options.
104+
type ExtendedOptions struct {
105+
Type string `json:"type,omitempty"`
106+
Options map[string]json.RawMessage `json:"options,omitempty"`
107+
}
108+
101109
// NamespaceOptions defines parameters for an M3DB namespace. See
102110
// https://m3db.github.io/m3/operational_guide/namespace_configuration/ for more
103111
// details.
@@ -131,4 +139,7 @@ type NamespaceOptions struct {
131139

132140
// AggregationOptions sets the aggregation parameters.
133141
AggregationOptions AggregationOptions `json:"aggregationOptions,omitempty"`
142+
143+
// ExtendedOptions stores the extended namespace options.
144+
ExtendedOptions *ExtendedOptions `json:"extendedOptions,omitempty"`
134145
}

pkg/apis/m3dboperator/v1alpha1/zz_generated.deepcopy.go

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/m3admin/namespace/namespace.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121
package namespace
2222

2323
import (
24+
"bytes"
25+
"encoding/json"
2426
"errors"
2527
"fmt"
2628
"time"
2729

2830
myspec "github.com/m3db/m3db-operator/pkg/apis/m3dboperator/v1alpha1"
2931

32+
"github.com/gogo/protobuf/jsonpb"
33+
pbtypes "github.com/gogo/protobuf/types"
3034
m3ns "github.com/m3db/m3/src/dbnode/generated/proto/namespace"
3135
"github.com/m3db/m3/src/query/generated/proto/admin"
3236
)
@@ -95,6 +99,11 @@ func m3dbNamespaceOptsFromSpec(opts *myspec.NamespaceOptions) (*m3ns.NamespaceOp
9599
return nil, err
96100
}
97101

102+
extOpts, err := m3dbExtendedOptsFromSpec(opts.ExtendedOptions)
103+
if err != nil {
104+
return nil, err
105+
}
106+
98107
return &m3ns.NamespaceOptions{
99108
BootstrapEnabled: opts.BootstrapEnabled,
100109
FlushEnabled: opts.FlushEnabled,
@@ -106,6 +115,7 @@ func m3dbNamespaceOptsFromSpec(opts *myspec.NamespaceOptions) (*m3ns.NamespaceOp
106115
IndexOptions: indexOpts,
107116
ColdWritesEnabled: opts.ColdWritesEnabled,
108117
AggregationOptions: aggOpts,
118+
ExtendedOptions: extOpts,
109119
}, nil
110120
}
111121

@@ -192,3 +202,33 @@ func m3dbAggregationOptsFromSpec(opts myspec.AggregationOptions) (*m3ns.Aggregat
192202
}, nil
193203

194204
}
205+
206+
func m3dbExtendedOptsFromSpec(opts *myspec.ExtendedOptions) (*m3ns.ExtendedOptions, error) {
207+
if opts == nil {
208+
return nil, nil
209+
}
210+
211+
pbStruct, err := pbStructFromSpec(opts.Options)
212+
if err != nil {
213+
return nil, err
214+
}
215+
216+
return &m3ns.ExtendedOptions{
217+
Type: opts.Type,
218+
Options: pbStruct,
219+
}, nil
220+
}
221+
222+
func pbStructFromSpec(opts map[string]json.RawMessage) (*pbtypes.Struct, error) {
223+
jsonBytes, err := json.Marshal(opts)
224+
if err != nil {
225+
return nil, err
226+
}
227+
228+
pbStruct := &pbtypes.Struct{}
229+
if err := jsonpb.Unmarshal(bytes.NewReader(jsonBytes), pbStruct); err != nil {
230+
return nil, err
231+
}
232+
233+
return pbStruct, nil
234+
}

pkg/m3admin/namespace/namespace_test.go

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@
2121
package namespace
2222

2323
import (
24+
"encoding/json"
2425
"testing"
2526
"time"
2627

2728
myspec "github.com/m3db/m3db-operator/pkg/apis/m3dboperator/v1alpha1"
2829

30+
pbtypes "github.com/gogo/protobuf/types"
2931
m3ns "github.com/m3db/m3/src/dbnode/generated/proto/namespace"
3032
"github.com/m3db/m3/src/query/generated/proto/admin"
31-
3233
"github.com/stretchr/testify/assert"
3334
"github.com/stretchr/testify/require"
3435
)
@@ -40,29 +41,25 @@ func TestRequestFromSpec(t *testing.T) {
4041
require.NoError(t, err)
4142

4243
tests := []struct {
44+
name string
4345
ns myspec.Namespace
4446
req *admin.NamespaceAddRequest
4547
expErr bool
4648
}{
4749
{
50+
name: "no fields",
4851
ns: myspec.Namespace{},
4952
expErr: true,
5053
},
5154
{
55+
name: "only name set",
5256
ns: myspec.Namespace{
5357
Name: "empty",
5458
},
5559
expErr: true,
5660
},
5761
{
58-
ns: myspec.Namespace{
59-
Name: "badpreset",
60-
Preset: "a",
61-
Options: &myspec.NamespaceOptions{},
62-
},
63-
expErr: true,
64-
},
65-
{
62+
name: "valid custom",
6663
ns: myspec.Namespace{
6764
Name: "validcustom",
6865
Options: &myspec.NamespaceOptions{
@@ -111,6 +108,7 @@ func TestRequestFromSpec(t *testing.T) {
111108
},
112109
},
113110
{
111+
name: "AggregatedOptions",
114112
ns: myspec.Namespace{
115113
Name: "aggregated",
116114
Options: &myspec.NamespaceOptions{
@@ -170,6 +168,72 @@ func TestRequestFromSpec(t *testing.T) {
170168
},
171169
},
172170
{
171+
name: "ExtendedOptions",
172+
ns: myspec.Namespace{
173+
Name: "extended",
174+
Options: &myspec.NamespaceOptions{
175+
BootstrapEnabled: true,
176+
WritesToCommitLog: false,
177+
RetentionOptions: myspec.RetentionOptions{
178+
RetentionPeriod: "1s",
179+
BlockSize: "1s",
180+
BufferFuture: "1s",
181+
BufferPast: "1s",
182+
BlockDataExpiry: true,
183+
BlockDataExpiryAfterNotAccessPeriod: "1s",
184+
},
185+
IndexOptions: myspec.IndexOptions{
186+
BlockSize: "1s",
187+
Enabled: true,
188+
},
189+
ExtendedOptions: &myspec.ExtendedOptions{
190+
Type: "testOpts",
191+
Options: map[string]json.RawMessage{
192+
"key1": json.RawMessage(`"str"`),
193+
"key2": json.RawMessage(`123`),
194+
"key3": json.RawMessage(`{
195+
"subKey1": "foo",
196+
"subKey2": "bar"
197+
}`),
198+
},
199+
},
200+
},
201+
},
202+
req: &admin.NamespaceAddRequest{
203+
Name: "extended",
204+
Options: &m3ns.NamespaceOptions{
205+
BootstrapEnabled: true,
206+
WritesToCommitLog: false,
207+
RetentionOptions: &m3ns.RetentionOptions{
208+
RetentionPeriodNanos: 1000000000,
209+
BlockSizeNanos: 1000000000,
210+
BufferFutureNanos: 1000000000,
211+
BufferPastNanos: 1000000000,
212+
BlockDataExpiry: true,
213+
BlockDataExpiryAfterNotAccessPeriodNanos: 1000000000,
214+
},
215+
IndexOptions: &m3ns.IndexOptions{
216+
BlockSizeNanos: 1000000000,
217+
Enabled: true,
218+
},
219+
ExtendedOptions: &m3ns.ExtendedOptions{
220+
Type: "testOpts",
221+
Options: &pbtypes.Struct{Fields: map[string]*pbtypes.Value{
222+
"key1": {Kind: &pbtypes.Value_StringValue{StringValue: "str"}},
223+
"key2": {Kind: &pbtypes.Value_NumberValue{NumberValue: 123}},
224+
"key3": {Kind: &pbtypes.Value_StructValue{StructValue: &pbtypes.Struct{
225+
Fields: map[string]*pbtypes.Value{
226+
"subKey1": {Kind: &pbtypes.Value_StringValue{StringValue: "foo"}},
227+
"subKey2": {Kind: &pbtypes.Value_StringValue{StringValue: "bar"}},
228+
},
229+
}}},
230+
}},
231+
},
232+
},
233+
},
234+
},
235+
{
236+
name: "invalid custom",
173237
ns: myspec.Namespace{
174238
Name: "invalidcustom",
175239
Options: &myspec.NamespaceOptions{
@@ -197,13 +261,24 @@ func TestRequestFromSpec(t *testing.T) {
197261
expErr: true,
198262
},
199263
{
264+
name: "bad preset 1",
265+
ns: myspec.Namespace{
266+
Name: "badpreset",
267+
Preset: "a",
268+
Options: &myspec.NamespaceOptions{},
269+
},
270+
expErr: true,
271+
},
272+
{
273+
name: "bad preset 2",
200274
ns: myspec.Namespace{
201275
Name: "foo",
202276
Preset: "a",
203277
},
204278
expErr: true,
205279
},
206280
{
281+
name: "preset 10s:2d",
207282
ns: myspec.Namespace{
208283
Name: "foo",
209284
Preset: "10s:2d",
@@ -214,6 +289,7 @@ func TestRequestFromSpec(t *testing.T) {
214289
},
215290
},
216291
{
292+
name: "preset 1m:40d",
217293
ns: myspec.Namespace{
218294
Name: "foo",
219295
Preset: "1m:40d",
@@ -226,24 +302,26 @@ func TestRequestFromSpec(t *testing.T) {
226302
}
227303

228304
for _, test := range tests {
229-
req, err := RequestFromSpec(test.ns)
230-
if test.expErr {
231-
assert.Error(t, err)
232-
} else {
233-
assert.NoError(t, err)
234-
assert.Equal(t, test.req, req)
235-
}
305+
t.Run(test.name, func(t *testing.T) {
306+
req, err := RequestFromSpec(test.ns)
307+
if test.expErr {
308+
assert.Error(t, err)
309+
} else {
310+
assert.NoError(t, err)
311+
assert.Equal(t, test.req, req)
312+
}
313+
})
236314
}
237315
}
238316

239317
func TestRetentionOptsFromAPI(t *testing.T) {
240318
opts := myspec.RetentionOptions{
241-
RetentionPeriod: time.Duration(time.Second).String(),
242-
BlockSize: time.Duration(2 * time.Second).String(),
243-
BufferFuture: time.Duration(3 * time.Second).String(),
244-
BufferPast: time.Duration(4 * time.Second).String(),
319+
RetentionPeriod: time.Second.String(),
320+
BlockSize: (2 * time.Second).String(),
321+
BufferFuture: (3 * time.Second).String(),
322+
BufferPast: (4 * time.Second).String(),
245323
BlockDataExpiry: true,
246-
BlockDataExpiryAfterNotAccessPeriod: time.Duration(5 * time.Second).String(),
324+
BlockDataExpiryAfterNotAccessPeriod: (5 * time.Second).String(),
247325
}
248326

249327
nsOpts, err := m3dbRetentionOptsFromSpec(opts)

0 commit comments

Comments
 (0)